/* * TV_Downstream_UserinfoChanged * * Pull specific info from a newly changed userinfo string * into a more C friendly form. */ void TV_Downstream_UserinfoChanged( client_t *client ) { char *val; assert( client ); assert( Info_Validate( client->userinfo ) ); // force the IP key/value pair so the game can filter based on ip if( !Info_SetValueForKey( client->userinfo, "socket", NET_SocketTypeToString( client->netchan.socket->type ) ) ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (socket)\n" ); return; } if( !Info_SetValueForKey( client->userinfo, "ip", NET_AddressToString( &client->netchan.remoteAddress ) ) ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (ip)\n" ); return; } // we handle name ourselves here, since tv module doesn't know about all the players val = TV_Downstream_FixName( Info_ValueForKey( client->userinfo, "name" ), client ); Q_strncpyz( client->name, val, sizeof( client->name ) ); if( !Info_SetValueForKey( client->userinfo, "name", client->name ) ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (name)" ); return; } if( client->relay ) TV_Relay_ClientUserinfoChanged( client->relay, client ); if( !Info_Validate( client->userinfo ) ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Invalid userinfo (after game)" ); return; } }
/* * TV_Downstream_DirectConnect * A upstream request that did not come from the master */ static void TV_Downstream_DirectConnect( const socket_t *socket, const netadr_t *address ) { #ifdef TCP_ALLOW_TVCONNECT int incoming = 0; #endif char userinfo[MAX_INFO_STRING], *name; client_t *cl, *newcl; int i, version, game_port, challenge; bool tv_client; version = atoi( Cmd_Argv( 1 ) ); if( version != APP_PROTOCOL_VERSION ) { if( version <= 6 ) { // before reject packet was added Netchan_OutOfBandPrint( socket, address, "print\nServer is version %4.2f. Protocol %3i\n", APP_VERSION, APP_PROTOCOL_VERSION ); } else { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer and client don't have the same version\n", DROP_TYPE_GENERAL, 0 ); } return; } game_port = atoi( Cmd_Argv( 2 ) ); challenge = atoi( Cmd_Argv( 3 ) ); tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? true : false ); if( !Info_Validate( Cmd_Argv( 4 ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nInvalid userinfo string\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: invalid userinfo string\n", NET_AddressToString( address ) ); return; } Q_strncpyz( userinfo, Cmd_Argv( 4 ), sizeof( userinfo ) ); // force the IP key/value pair so the game can filter based on ip if( !Info_SetValueForKey( userinfo, "socket", NET_SocketTypeToString( socket->type ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (socket)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (socket)\n", NET_AddressToString( address ) ); return; } if( !Info_SetValueForKey( userinfo, "ip", NET_AddressToString( address ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (ip)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) ); return; } // we handle name ourselves here, since tv module doesn't know about all the players name = TV_Downstream_FixName( Info_ValueForKey( userinfo, "name" ), NULL ); if( !Info_SetValueForKey( userinfo, "name", name ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (name)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (name)\n", NET_AddressToString( address ) ); return; } #ifdef TCP_ALLOW_TVCONNECT if( socket->type == SOCKET_TCP ) { // find the upstream for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !tvs.incoming[i].active ) continue; if( NET_CompareAddress( &tvs.incoming[i].address, address ) && socket == &tvs.incoming[i].socket ) break; } if( i == MAX_INCOMING_CONNECTIONS ) { Com_Error( ERR_FATAL, "Incoming upstream not found.\n" ); return; } incoming = i; } #endif // see if the challenge is valid for( i = 0; i < MAX_CHALLENGES; i++ ) { if( NET_CompareBaseAddress( address, &tvs.challenges[i].adr ) ) { if( challenge == tvs.challenges[i].challenge ) { tvs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge tvs.challenges[i].time = 0; NET_InitAddress( &tvs.challenges[i].adr, NA_NOTRANSMIT ); break; // good } Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nBad challenge\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } } if( i == MAX_CHALLENGES ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nNo challenge for address\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } newcl = NULL; // if there is already a slot for this ip, reuse it for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareAddress( address, &cl->netchan.remoteAddress ) || ( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) && cl->netchan.game_port == game_port ) ) { if( !NET_IsLocalAddress( address ) && ( tvs.realtime - cl->lastconnect ) < (unsigned)( tv_reconnectlimit->integer * 1000 ) ) { return; } newcl = cl; break; } } // find a client slot if( !newcl ) { for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) { newcl = cl; break; } } if( !newcl ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } } // get the game a chance to reject this upstream or modify the userinfo if( !TV_Downstream_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, tv_client ) ) { char *rejtypeflag, *rejmsg; // hax because Info_ValueForKey can only be called twice in a row rejtypeflag = va( "%s\n%s", Info_ValueForKey( userinfo, "rejtype" ), Info_ValueForKey( userinfo, "rejflag" ) ); rejmsg = Info_ValueForKey( userinfo, "rejmsg" ); Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg ); return; } // send the connect packet to the client Netchan_OutOfBandPrint( socket, address, "client_connect" ); // free the incoming entry #ifdef TCP_ALLOW_TVCONNECT if( socket->type == SOCKET_TCP ) { tvs.incoming[incoming].active = false; tvs.incoming[incoming].socket.open = false; } #endif }