diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-12-23 20:46:54 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-12-23 20:46:54 +0000 |
commit | f1be065ea60bbb97f6d21477fc45e49bbb29cd71 (patch) | |
tree | 8807bcb0590abc3e1472a3b977c3d112498acd23 | |
parent | b1fa1be8b67e6d34a3d11fe4bfb9cd27191e2b28 (diff) |
Added support for parsing anticheat cvars.txt and hashes.txt.
Properly parse multiple AC server messages received at once.
Added `--enable-anticheat' option to configure script.
-rwxr-xr-x | configure | 9 | ||||
-rw-r--r-- | source/com_local.h | 16 | ||||
-rw-r--r-- | source/mvd_client.c | 8 | ||||
-rw-r--r-- | source/q_shared.c | 57 | ||||
-rw-r--r-- | source/q_shared.h | 4 | ||||
-rw-r--r-- | source/sv_ac.c | 411 | ||||
-rw-r--r-- | source/sv_http.c | 25 |
7 files changed, 438 insertions, 92 deletions
@@ -41,6 +41,7 @@ executables="" libraries="" hardlink="no" asm="no" +anticheat="no" gldriver="libGL.so" indriver="" homedir=".q2pro" @@ -151,6 +152,8 @@ for opt do ;; --disable-asm) asm="no" ;; + --enable-anticheat) anticheat="yes" + ;; *) echo "Unknown option: $opt (try --help)" && exit 1 ;; esac @@ -180,6 +183,7 @@ echo " --disable-zlib disable linking with zlib" echo " --enable-png enable linking with PNG library" echo " --enable-jpeg enable linking with JPEG library" echo " --disable-asm disable i386 assembly optimizations" +echo " --enable-anticheat enable r1ch.net anticheat server interface" echo "" echo "Object files are built in the directory from which configure is run." exit 1 @@ -437,6 +441,11 @@ if [ "$asm" = "yes" ]; then echo "#define USE_ASM 1" >> $config_h fi +if [ "$anticheat" = "yes" ]; then + echo "USE_ANTICHEAT=yes" >> $config_mk + echo "#define USE_ANTICHEAT 2" >> $config_h +fi + echo "#define USE_MAPCHECKSUM 1" >> $config_h echo "#define USE_AUTOREPLY 1" >> $config_h diff --git a/source/com_local.h b/source/com_local.h index ed45dbf..10df295 100644 --- a/source/com_local.h +++ b/source/com_local.h @@ -382,19 +382,19 @@ int FIFO_Read( fifo_t *fifo, void *buffer, int length ); int FIFO_Write( fifo_t *fifo, const void *buffer, int length ); static inline qboolean FIFO_TryRead( fifo_t *fifo, void *buffer, int length ) { - if( FIFO_Read( fifo, NULL, length ) == length ) { - FIFO_Read( fifo, buffer, length ); - return qtrue; + if( FIFO_Read( fifo, NULL, length ) < length ) { + return qfalse; } - return qfalse; + FIFO_Read( fifo, buffer, length ); + return qtrue; } static inline qboolean FIFO_TryWrite( fifo_t *fifo, void *buffer, int length ) { - if( FIFO_Write( fifo, NULL, length ) == length ) { - FIFO_Write( fifo, buffer, length ); - return qtrue; + if( FIFO_Write( fifo, NULL, length ) < length ) { + return qfalse; } - return qfalse; + FIFO_Write( fifo, buffer, length ); + return qtrue; } /* diff --git a/source/mvd_client.c b/source/mvd_client.c index 4b240eb..e1ae2c6 100644 --- a/source/mvd_client.c +++ b/source/mvd_client.c @@ -559,7 +559,7 @@ static qboolean MVD_ParseResponse( mvd_t *mvd ) { if( !mvd->statusCode ) { // parse version - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( !token[0] ) { continue; // empty line? } @@ -568,7 +568,7 @@ static qboolean MVD_ParseResponse( mvd_t *mvd ) { } // parse status code - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); mvd->statusCode = atoi( token ); if( !mvd->statusCode ) { MVD_Dropf( mvd, "Malformed HTTP status code" ); @@ -583,7 +583,7 @@ static qboolean MVD_ParseResponse( mvd_t *mvd ) { } } else { // parse header fields - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( !token[0] ) { return qtrue; // end of header } @@ -595,7 +595,7 @@ static qboolean MVD_ParseResponse( mvd_t *mvd ) { *p = 0; Q_strlwr( key ); - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( !strcmp( key, "content-type" ) ) { } else if( !strcmp( key, "content-encoding" ) ) { #if USE_ZLIB diff --git a/source/q_shared.c b/source/q_shared.c index 4b4a519..97f49c8 100644 --- a/source/q_shared.c +++ b/source/q_shared.c @@ -1479,7 +1479,12 @@ char *va( const char *format, ... ) { } -static char com_token[MAX_TOKEN_CHARS]; +static char com_token[4][MAX_TOKEN_CHARS]; +static int com_tokidx; + +static char *COM_GetToken( void ) { + return com_token[com_tokidx++ & 3]; +} /* ============== @@ -1488,25 +1493,29 @@ COM_SimpleParse Parse a token out of a string. ============== */ -char *COM_SimpleParse( const char **data_p ) { +char *COM_SimpleParse( const char **data_p, int *length ) { int c; int len; const char *data; + char *s = COM_GetToken(); data = *data_p; len = 0; - com_token[0] = 0; + s[0] = 0; + if( length ) { + *length = 0; + } if( !data ) { *data_p = NULL; - return com_token; + return s; } // skip whitespace while( ( c = *data ) <= ' ' ) { if( c == 0 ) { *data_p = NULL; - return com_token; + return s; } data++; } @@ -1514,17 +1523,20 @@ char *COM_SimpleParse( const char **data_p ) { // parse a regular word do { if( len < MAX_TOKEN_CHARS - 1 ) { - com_token[len] = c; - len++; + s[len++] = c; } data++; c = *data; } while( c > 32 ); - com_token[len] = 0; + s[len] = 0; + + if( length ) { + *length = len; + } *data_p = data; - return com_token; + return s; } @@ -1540,14 +1552,15 @@ char *COM_Parse( const char **data_p ) { int c; int len; const char *data; + char *s = COM_GetToken(); data = *data_p; len = 0; - com_token[0] = 0; + s[0] = 0; if( !data ) { *data_p = NULL; - return com_token; + return s; } // skip whitespace @@ -1555,7 +1568,7 @@ skipwhite: while( ( c = *data ) <= ' ' ) { if( c == 0 ) { *data_p = NULL; - return com_token; + return s; } data++; } @@ -1591,8 +1604,7 @@ skipwhite: } if( len < MAX_TOKEN_CHARS - 1 ) { - com_token[len] = c; - len++; + s[len++] = c; } } } @@ -1600,18 +1612,17 @@ skipwhite: // parse a regular word do { if( len < MAX_TOKEN_CHARS - 1 ) { - com_token[len] = c; - len++; + s[len++] = c; } data++; c = *data; } while( c > 32 ); finish: - com_token[len] = 0; + s[len] = 0; *data_p = data; - return com_token; + return s; } /* @@ -1931,6 +1942,16 @@ int Com_sprintf( char *dest, int destsize, const char *fmt, ... ) { return ret; } +char *Q_strchrnul( const char *s, int c ) { + while( *s ) { + if( *s == c ) { + return ( char * )s; + } + s++; + } + return ( char * )s; +} + /* ===================================================================== diff --git a/source/q_shared.h b/source/q_shared.h index f1bd728..a66228f 100644 --- a/source/q_shared.h +++ b/source/q_shared.h @@ -457,6 +457,8 @@ int Q_strncasecmp( const char *s1, const char *s2, int n ); int QDECL SortStrcmp( const void *p1, const void *p2 ); +char *Q_strchrnul( const char *s, int c ); + char *COM_SkipPath( const char *pathname ); void COM_StripExtension( const char *in, char *out, int outSize ); void COM_FileBase (char *in, char *out); @@ -468,7 +470,7 @@ char *COM_FileExtension( const char *in ); qboolean COM_IsNumeric( const char *string ); qboolean COM_HasSpaces( const char *string ); -char *COM_SimpleParse( const char **data_p ); +char *COM_SimpleParse( const char **data_p, int *length ); char *COM_Parse( const char **data_p ); // data is an in/out parm, returns a parsed out token int COM_Compress( char *data ); diff --git a/source/sv_ac.c b/source/sv_ac.c index 9798532..25cbbaf 100644 --- a/source/sv_ac.c +++ b/source/sv_ac.c @@ -36,7 +36,7 @@ typedef enum { ACS_PONG, ACS_UPDATE_REQUIRED, ACS_DISCONNECT, - ACS_ERROR, + ACS_ERROR } ac_serverbyte_t; typedef enum { @@ -48,7 +48,7 @@ typedef enum { ACC_QUERYCLIENT, ACC_PING, ACC_UPDATECHECKS, - ACC_SETPREFERENCES, + ACC_SETPREFERENCES } ac_clientbyte_t; typedef enum { @@ -61,7 +61,7 @@ typedef enum { OP_GT, OP_STREQUAL, OP_STRNEQUAL, - OP_STRSTR, + OP_STRSTR } ac_opcode_t; typedef enum { @@ -84,8 +84,7 @@ typedef struct ac_cvar_s { int num_values; char **values; ac_opcode_t op; - char *def; - char name[1]; + char *def, *name; } ac_cvar_t; typedef struct { @@ -95,14 +94,20 @@ typedef struct { unsigned last_ping; netstream_t stream; int msglen; +} ac_locals_t; + +typedef struct { + time_t retry_time; + int retry_backoff; ac_file_t *files; int num_files; ac_cvar_t *cvars; int num_cvars; -} ac_locals_t; + char hashlist_name[MAX_QPATH]; +} ac_static_t; #define ACP_BLOCKPLAY ( 1 << 0 ) @@ -122,13 +127,11 @@ typedef struct { #define AC_RECV_SIZE 1024 static ac_locals_t ac; +static ac_static_t acs; static list_t ac_required_list; static list_t ac_exempt_list; -static time_t ac_retry_time; -static int ac_retry_backoff; - static byte ac_send_buffer[AC_SEND_SIZE]; static byte ac_recv_buffer[AC_RECV_SIZE]; @@ -149,11 +152,289 @@ static const char ac_clients[][8] = { "EGL", "Apr GL", "Apr SW", - "Q2PRO", + "Q2PRO" }; static const int ac_num_clients = sizeof( ac_clients ) / sizeof( ac_clients[0] ); + +/* +============================================================================== + +FILE PARSING + +============================================================================== +*/ + +#define AC_HASHES_NAME "anticheat-hashes.txt" +#define AC_CVARS_NAME "anticheat-cvars.txt" +#define AC_TOKENS_NAME "anticheat-tokens.txt" + +typedef struct { + char str[4]; + ac_opcode_t code; + int max_values; +} ac_cvarop_t; + +static const ac_cvarop_t ac_cvarops[] = { + { "=", OP_EQUAL, 254 }, + { "==", OP_EQUAL, 254 }, + { "!=", OP_NEQUAL, 254 }, + { ">=", OP_GTEQUAL, 1 }, + { "<=", OP_LTEQUAL, 1 }, + { "<", OP_LT, 1 }, + { ">", OP_GT, 1 }, + { "eq", OP_STREQUAL, 254 }, + { "ne", OP_STRNEQUAL, 254 }, + { "~", OP_STRSTR, 254 }, + { 0 } +}; + + +static void AC_ParseHash( const char *data, int linenum, const char *path ) { + char *pstr, *hstr; + int pathlen, hashlen; + int flags; + byte hash[20]; + ac_file_t *file; + int i; + + if( *data == '!' ) { + Q_strncpyz( acs.hashlist_name, data + 1, sizeof( acs.hashlist_name ) ); + return; + } + + pstr = COM_SimpleParse( &data, &pathlen ); + if( !pstr[0] ) { + return; + } + hstr = COM_SimpleParse( &data, &hashlen ); + if( !hstr[0] ) { + Com_WPrintf( "ANTICHEAT: Incomplete line %d in %s\n", linenum, path ); + return; + } + + if( pathlen >= MAX_QPATH ) { + Com_WPrintf( "ANTICHEAT: Too long quake path on line %d in %s\n", linenum, path ); + return; + } + if( strchr( pstr, '\\' ) || !Q_isalnum( pstr[0] ) ) { + Com_WPrintf( "ANTICHEAT: Malformed quake path on line %d in %s\n", linenum, path ); + return; + } + + if( hashlen != 40 ) { +badhash: + Com_WPrintf( "ANTICHEAT: Malformed hash on line %d in %s\n", linenum, path ); + return; + } + + for( i = 0; i < 20; i++, hstr += 2 ) { + int c1 = Q_charhex( hstr[0] ); + int c2 = Q_charhex( hstr[1] ); + if( c1 == -1 || c2 == -1 ) { + goto badhash; + } + hash[i] = ( c1 << 4 ) | c2; + } + + // parse optional flags + flags = 0; + if( data ) { + if( strstr( data, "required" ) ) { + flags |= ACH_REQUIRED; + } + if( strstr( data, "negative" ) ) { + flags |= ACH_NEGATIVE; + } + } + + file = SV_Malloc( sizeof( *file ) + pathlen ); + memcpy( file->hash, hash, sizeof( file->hash ) ); + memcpy( file->path, pstr, pathlen + 1 ); + file->flags = flags; + file->next = acs.files; + acs.files = file; + acs.num_files++; +} + +static void AC_ParseCvar( const char *data, int linenum, const char *path ) { + char *values[256], *p; + byte lengths[256]; + char *name, *opstr, *val, *def; + int num_values, namelen, vallen, deflen; + ac_cvar_t *cvar; + const ac_cvarop_t *op; + int i, len; + + name = COM_SimpleParse( &data, &namelen ); + if( !name[0] ) { + return; + } + opstr = COM_SimpleParse( &data, NULL ); + val = COM_SimpleParse( &data, &vallen ); + def = COM_SimpleParse( &data, &deflen ); + if( !opstr[0] || !val[0] || !def[0] ) { + Com_WPrintf( "ANTICHEAT: Incomplete line %d in %s\n", linenum, path ); + return; + } + + if( namelen >= 64 ) { + Com_WPrintf( "ANTICHEAT: Too long cvar name on line %d in %s\n", linenum, path ); + return; + } + if( deflen >= 64 ) { + Com_WPrintf( "ANTICHEAT: Too long default value on line %d in %s\n", linenum, path ); + return; + } + + for( op = ac_cvarops; op->str[0]; op++ ) { + if( !strcmp( opstr, op->str ) ) { + break; + } + } + if( !op->str[0] ) { + Com_WPrintf( "ANTICHEAT: Unknown opcode '%s' on line %d in %s\n", opstr, linenum, path ); + return; + } + + num_values = 0; + while( 1 ) { + if( num_values == op->max_values ) { + Com_WPrintf( "ANTICHEAT: Too many values for opcode '%s' on line %d in %s\n", opstr, linenum, path ); + return; + } + if( !val[0] ) { + Com_WPrintf( "ANTICHEAT: Empty value on line %d in %s\n", linenum, path ); + return; + } + p = strchr( val, ',' ); + if( p ) { + *p = 0; + } + len = strlen( val ); + if( len >= 64 ) { + Com_WPrintf( "ANTICHEAT: Too long value on line %d in %s\n", linenum, path ); + return; + } + values[num_values] = val; + lengths[num_values++] = len + 1; + if( !p ) { + break; + } + val = p + 1; + } + + Z_TagReserve( sizeof( *cvar ) + namelen + 1 + deflen + 1 + + num_values * sizeof( char * ) + vallen + 1, TAG_SERVER ); + cvar = Z_ReservedAlloc( sizeof( *cvar ) ); + cvar->name = Z_ReservedAlloc( namelen + 1 ); + memcpy( cvar->name, name, namelen + 1 ); + cvar->def = Z_ReservedAlloc( deflen + 1 ); + memcpy( cvar->def, def, deflen + 1 ); + cvar->num_values = num_values; + cvar->values = Z_ReservedAlloc( num_values * sizeof( char * ) ); + for( i = 0; i < num_values; i++ ) { + cvar->values[i] = Z_ReservedAlloc( lengths[i] ); + memcpy( cvar->values[i], values[i], lengths[i] ); + } + cvar->op = op->code; + cvar->next = acs.cvars; + acs.cvars = cvar; + acs.num_cvars++; +} + +static qboolean AC_ParseFile( const char *path, void (*parse)( const char *, int, const char * ) ) { + char *data, *p; + int linenum = 1; + + FS_LoadFile( path, ( void ** )&data ); + if( !data ) { + return qfalse; + } + + while( *data ) { + p = strchr( data, '\n' ); + if( p ) { + *p = 0; + } + + switch( *data ) { + case '/': + case '#': + case 0: + break; + case '\\': + if( !strncmp( data + 1, "include ", 8 ) ) { + if( !AC_ParseFile( data + 9, parse ) ) { + Com_WPrintf( "ANTICHEAT: Could not include %s\n", data + 9 ); + } + } else { + Com_WPrintf( "ANTICHEAT: Unknown directive %s on line %d in %s\n", data + 1, linenum, path ); + } + break; + default: + parse( data, linenum, path ); + break; + } + + if( !p ) { + break; + } + + linenum++; + data = p + 1; + } + + return qtrue; +} + +static void AC_LoadChecks( void ) { + if( !AC_ParseFile( AC_HASHES_NAME, AC_ParseHash ) ) { + Com_Printf( "ANTICHEAT: Missing " AC_HASHES_NAME ", " + "not using any file checks.\n" ); + strcpy( acs.hashlist_name, "none" ); + } else if( !acs.num_files ) { + Com_Printf( "ANTICHEAT: No file hashes were loaded, " + "please check the " AC_HASHES_NAME ".\n" ); + strcpy( acs.hashlist_name, "none" ); + } else if( !acs.hashlist_name[0] ) { + Com_sprintf( acs.hashlist_name, MAX_QPATH, "unknown (%d %s)", + acs.num_files, acs.num_files == 1 ? "entry" : "entries" ); + } + + if( !AC_ParseFile( AC_CVARS_NAME, AC_ParseCvar ) ) { + Com_Printf( "ANTICHEAT: Missing " AC_CVARS_NAME ", " + "not using any cvar checks.\n" ); + } else if( !acs.num_cvars ) { + Com_Printf( "ANTICHEAT: No cvar checks were loaded, " + "please check the " AC_CVARS_NAME ".\n" ); + } + + //AC_ParseFile( "anticheat-tokens.txt", AC_ParseToken ); +} + +static void AC_FreeChecks( void ) { + ac_file_t *f, *fn; + ac_cvar_t *c, *cn; + + for( f = acs.files; f; f = fn ) { + fn = f->next; + Z_Free( f ); + } + acs.files = NULL; + + for( c = acs.cvars; c; c = cn ) { + cn = c->next; + Z_Free( c ); + } + acs.cvars = NULL; + + acs.hashlist_name[0] = 0; + acs.num_files = 0; + acs.num_cvars = 0; +} + /* ============================================================================== @@ -170,10 +451,10 @@ static void AC_Drop( void ) { if( !ac.connected ) { Com_Printf( "ANTICHEAT: Server connection failed. " - "Retrying in %d seconds.\n", ac_retry_backoff ); + "Retrying in %d seconds.\n", acs.retry_backoff ); clock = time( NULL ); - ac_retry_time = clock + ac_retry_backoff; - ac_retry_backoff += 5; + acs.retry_time = clock + acs.retry_backoff; + acs.retry_backoff += 5; return; } @@ -193,17 +474,17 @@ static void AC_Drop( void ) { "You will need to reconnect once the server has " "re-established the anticheat connection.\n" ); } - ac_retry_backoff = AC_DEFAULT_BACKOFF; + acs.retry_backoff = AC_DEFAULT_BACKOFF; } else { - ac_retry_backoff += 30; // this generally indicates a server problem + acs.retry_backoff += 30; // this generally indicates a server problem } Com_WPrintf( "ANTICHEAT: Lost connection to anticheat server! " - "Will attempt to reconnect in %d seconds.\n", ac_retry_backoff ); + "Will attempt to reconnect in %d seconds.\n", acs.retry_backoff ); clock = time( NULL ); - ac_retry_time = clock + ac_retry_backoff; + acs.retry_time = clock + acs.retry_backoff; memset( &ac, 0, sizeof( ac ) ); } @@ -337,7 +618,7 @@ static void AC_ParseClientAck( void ) { return; } - if( cl->state != cs_connected && cl->state != cs_primed ) { + if( cl->state < cs_connected || cl->state > cs_primed ) { Com_DPrintf( "ANTICHEAT: %s with client in state %d\n", __func__, cl->state ); return; @@ -368,7 +649,7 @@ static void AC_ParseFileViolation( void ) { cl->ac_file_failures++; action = ac_badfile_action->integer; - for( f = ac.files; f; f = f->next ) { + for( f = acs.files; f; f = f->next ) { if( !strcmp( f->path, path ) ) { if( f->flags & ACH_REQUIRED ) { action = 1; @@ -425,7 +706,7 @@ static void AC_ParseFileViolation( void ) { static void AC_ParseReady( void ) { ac.ready = qtrue; ac.last_ping = svs.realtime; - ac_retry_backoff = AC_DEFAULT_BACKOFF; + acs.retry_backoff = AC_DEFAULT_BACKOFF; Com_Printf( "ANTICHEAT: Ready to serve anticheat clients.\n" ); Cvar_FullSet( "anticheat", ac_required->string, CVAR_SERVERINFO | CVAR_NOSET, CVAR_SET_DIRECT ); @@ -485,7 +766,7 @@ static void AC_ParseError( void ) { AC_Disconnect(); } -static void AC_ParseMessage( void ) { +static qboolean AC_ParseMessage( void ) { uint16 msglen; byte *data; int length; @@ -494,16 +775,16 @@ static void AC_ParseMessage( void ) { // parse msglen if( !ac.msglen ) { if( !FIFO_TryRead( &ac.stream.recv, &msglen, 2 ) ) { - return; + return qfalse; } if( !msglen ) { - return; + return qtrue; } msglen = LittleShort( msglen ); if( msglen > AC_RECV_SIZE ) { Com_EPrintf( "ANTICHEAT: Oversize message: %d bytes\n", msglen ); AC_Drop(); - return; + return qfalse; } ac.msglen = msglen; } @@ -512,7 +793,7 @@ static void AC_ParseMessage( void ) { data = FIFO_Peek( &ac.stream.recv, &length ); if( length < ac.msglen ) { if( !FIFO_TryRead( &ac.stream.recv, msg_read_buffer, ac.msglen ) ) { - return; // not yet available + return qfalse; // not yet available } SZ_Init( &msg_read, msg_read_buffer, sizeof( msg_read_buffer ) ); } else { @@ -542,22 +823,22 @@ static void AC_ParseMessage( void ) { break; case ACS_ERROR: AC_ParseError(); - break; + return qfalse; case ACS_NOACCESS: Com_WPrintf( "ANTICHEAT: You do not have permission to " "use the anticheat server. Anticheat disabled.\n" ); AC_Disconnect(); - break; + return qfalse; case ACS_UPDATE_REQUIRED: Com_WPrintf( "ANTICHEAT: The anticheat server is no longer " "compatible with this version of " APPLICATION ". " "Please make sure you are using the latest " APPLICATION " version. " "Anticheat disabled.\n" ); AC_Disconnect(); - break; + return qfalse; case ACS_DISCONNECT: AC_ParseDisconnect(); - break; + return qfalse; case ACS_PONG: ac.ping_pending = qfalse; ac.last_ping = svs.realtime; @@ -567,9 +848,10 @@ static void AC_ParseMessage( void ) { "sure you are using the latest " APPLICATION " version. " "Anticheat disabled.\n", cmd ); AC_Disconnect(); - break; + return qfalse; } + return qtrue; } /* @@ -789,11 +1071,11 @@ static void AC_SendChecks( void ) { MSG_WriteShort( 9 ); MSG_WriteByte( ACC_UPDATECHECKS ); - MSG_WriteLong( ac.num_files ); - MSG_WriteLong( ac.num_cvars ); + MSG_WriteLong( acs.num_files ); + MSG_WriteLong( acs.num_cvars ); AC_Flush(); - for( f = ac.files, p = NULL; f; p = f, f = f->next ) { + for( f = acs.files, p = NULL; f; p = f, f = f->next ) { MSG_WriteData( f->hash, sizeof( f->hash ) ); MSG_WriteByte( f->flags ); if( p && !strcmp( f->path, p->path ) ) { @@ -804,7 +1086,7 @@ static void AC_SendChecks( void ) { AC_Flush(); } - for( c = ac.cvars; c; c = c->next ) { + for( c = acs.cvars; c; c = c->next ) { AC_WriteString( c->name ); MSG_WriteByte( c->op ); MSG_WriteByte( c->num_values ); @@ -920,14 +1202,14 @@ static qboolean AC_Reconnect( void ) { ac.stream.send.size = AC_SEND_SIZE; ac.stream.recv.data = ac_recv_buffer; ac.stream.recv.size = AC_RECV_SIZE; - ac_retry_time = 0; + acs.retry_time = 0; return qtrue; fail: - ac_retry_backoff += 60; - Com_Printf( "ANTICHEAT: Retrying in %d seconds.\n", ac_retry_backoff ); + acs.retry_backoff += 60; + Com_Printf( "ANTICHEAT: Retrying in %d seconds.\n", acs.retry_backoff ); clock = time( NULL ); - ac_retry_time = clock + ac_retry_backoff; + acs.retry_time = clock + acs.retry_backoff; return qfalse; } @@ -936,9 +1218,9 @@ void AC_Run( void ) { neterr_t ret; time_t clock; - if( ac_retry_time ) { + if( acs.retry_time ) { clock = time( NULL ); - if( ac_retry_time < clock ) { + if( acs.retry_time < clock ) { Com_Printf( "ANTICHEAT: Attempting to reconnect to anticheat server...\n" ); AC_Reconnect(); } @@ -968,7 +1250,8 @@ void AC_Run( void ) { ac.connected = qtrue; AC_SendHello(); } - AC_ParseMessage(); + while( AC_ParseMessage() ) + ; AC_CheckTimeouts(); break; } @@ -989,10 +1272,12 @@ void AC_Connect( void ) { } #endif + AC_LoadChecks(); + Com_Printf( "ANTICHEAT: Attempting to connect to %s...\n", ac_server_address->string ); Sys_RunConsole(); - ac_retry_backoff = AC_DEFAULT_BACKOFF; + acs.retry_backoff = AC_DEFAULT_BACKOFF; if( !AC_Reconnect() ) { return; } @@ -1003,18 +1288,20 @@ void AC_Connect( void ) { Sys_Sleep( 1 ); AC_Run(); if( ac.ready || !ac.stream.state ) { - break; + return; } } + + Com_WPrintf( "ANTICHEAT: Still not ready, resuming server initialization.\n" ); } void AC_Disconnect( void ) { NET_Close( &ac.stream ); - ac_retry_time = 0; - ac_retry_backoff = AC_DEFAULT_BACKOFF; + AC_FreeChecks(); memset( &ac, 0, sizeof( ac ) ); + memset( &acs, 0, sizeof( acs ) ); Cvar_SetByVar( ac_required, "0", CVAR_SET_DIRECT ); Cvar_FullSet( "anticheat", "0", CVAR_NOSET, CVAR_SET_DIRECT ); } @@ -1024,6 +1311,11 @@ void AC_List_f( void ) { char *sub; int i; + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + if( !ac_required->integer ) { Com_Printf( "The anticheat module is not in use on this server.\n" "For information on anticheat, please visit http://antiche.at/\n" ); @@ -1062,6 +1354,10 @@ void AC_List_f( void ) { Com_Printf( "+----------------+--------+-----+------+\n" ); + if( ac.ready ) { + Com_Printf( "File check list in use: %s\n", acs.hashlist_name ); + } + Com_Printf( "This Quake II server is %sconnected to the anticheat server.\n" "For information on anticheat, please visit http://antiche.at/\n", @@ -1072,6 +1368,11 @@ void AC_Info_f( void ) { //client_t *cl; //linkednamelist_t *bad; + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + if( !ac_required->integer ) { Com_Printf( "The anticheat module is not in use on this server.\n" "For information on anticheat, please visit http://antiche.at/\n" ); @@ -1100,6 +1401,10 @@ void AC_Info_f( void ) { static void AC_Invalidate_f( void ) { client_t *cl; + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } if( !ac.ready ) { Com_Printf( "Anticheat is not ready.\n" ); return; @@ -1115,12 +1420,22 @@ static void AC_Invalidate_f( void ) { } static void AC_Update_f( void ) { - if( !ac.ready ) { - Com_Printf( "Anticheat is not ready.\n" ); + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + if( !ac_required->integer ) { + Com_Printf( "Anticheat is not in use.\n" ); return; + } + + AC_FreeChecks(); + AC_LoadChecks(); + + if( ac.connected ) { + AC_SendChecks(); } - AC_SendChecks(); Com_Printf( "Anticheat configuration updated.\n" ); } diff --git a/source/sv_http.c b/source/sv_http.c index d3c9d72..a7da6f2 100644 --- a/source/sv_http.c +++ b/source/sv_http.c @@ -249,16 +249,15 @@ void SV_HttpWrite( tcpClient_t *client, void *data, int length ) { #if USE_ZLIB if( client->z.state ) { z_streamp z = &client->z; - int param; + int param = Z_NO_FLUSH; - z->next_in = data; - z->avail_in = length; - - param = Z_NO_FLUSH; if( client->noflush > 120 ) { param = Z_SYNC_FLUSH; } + z->next_in = data; + z->avail_in = length; + while( z->avail_in ) { data = FIFO_Reserve( fifo, &length ); if( !length ) { @@ -275,7 +274,7 @@ void SV_HttpWrite( tcpClient_t *client, void *data, int length ) { } length -= z->avail_out; - if( length ) { + if( length > 0 ) { FIFO_Commit( fifo, length ); client->noflush = 0; } @@ -283,7 +282,7 @@ void SV_HttpWrite( tcpClient_t *client, void *data, int length ) { return; } #else - if( FIFO_Write( fifo, data, length ) != length ) { + if( !FIFO_TryWrite( fifo, data, length ) ) { SV_HttpDrop( client, "overflowed" ); } #endif @@ -500,7 +499,7 @@ static qboolean SV_HttpParseRequest( tcpClient_t *client ) { if( !client->method ) { // parse request line - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( !token[0] ) { continue; // ignore empty lines } @@ -518,7 +517,7 @@ static qboolean SV_HttpParseRequest( tcpClient_t *client ) { } // parse URI - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( !Q_stricmpn( token, "http://", 7 ) ) { p = strchr( token + 7, '/' ); if( !p ) { @@ -537,7 +536,7 @@ static qboolean SV_HttpParseRequest( tcpClient_t *client ) { } // parse version - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( strncmp( token, "HTTP/", 5 ) ) { SV_HttpReject( "400 Bad Request", NULL ); break; @@ -553,7 +552,7 @@ static qboolean SV_HttpParseRequest( tcpClient_t *client ) { break; } } else { - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( !token[0] ) { if( !client->host || !client->resource ) { SV_HttpReject( "400 Bad Request", NULL ); @@ -571,7 +570,7 @@ static qboolean SV_HttpParseRequest( tcpClient_t *client ) { *p = 0; Q_strlwr( key ); - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( !strcmp( key, "host" ) ) { if( !client->host ) { client->host = SV_CopyString( token ); @@ -583,7 +582,7 @@ static qboolean SV_HttpParseRequest( tcpClient_t *client ) { if( Q_stricmp( token, "Basic" ) ) { continue; } - token = COM_SimpleParse( &line ); + token = COM_SimpleParse( &line, NULL ); if( Q_Decode64( key, token, sizeof( key ) ) == -1 ) { continue; } |