/* * TV_Upstream_ServerReconnect_f * * The server is changing levels */ static void TV_Upstream_ServerReconnect_f( upstream_t *upstream ) { if( upstream->demo.playing ) return; if( upstream->state < CA_CONNECTED ) { Com_Printf( "%s: reconnect request while not connected\n", NET_AddressToString( &upstream->serveraddress ) ); return; } if( upstream->demo.recording ) TV_Upstream_StopDemoRecord( upstream, upstream->demo.autorecording, qfalse ); Com_Printf( "%s: reconnecting...\n", NET_AddressToString( &upstream->serveraddress ) ); upstream->connect_count = 0; upstream->rejected = 0; #ifdef TCP_ALLOW_CONNECT upstream->connect_time = tvs.realtime; #else upstream->connect_time = tvs.realtime - 1500; #endif upstream->state = CA_HANDSHAKE; TV_Upstream_AddReliableCommand( upstream, "new" ); }
/* * SV_ConnectionlessPacket * * A connectionless packet has four leading 0xff * characters to distinguish it from a game channel. * Clients that are in the game can still send * connectionless packets. */ void SV_ConnectionlessPacket( const socket_t *socket, const netadr_t *address, msg_t *msg ) { connectionless_cmd_t *cmd; char *s, *c; MSG_BeginReading( msg ); MSG_ReadLong( msg ); // skip the -1 marker s = MSG_ReadStringLine( msg ); Cmd_TokenizeString( s ); c = Cmd_Argv( 0 ); Com_DPrintf( "Packet %s : %s\n", NET_AddressToString( address ), c ); for( cmd = connectionless_cmds; cmd->name; cmd++ ) { if( !strcmp( c, cmd->name ) ) { cmd->func( socket, address ); return; } } Com_DPrintf( "Bad connectionless packet from %s:\n%s\n", NET_AddressToString( address ), s ); }
/* * TV_Upstream_Status_f */ static void TV_Upstream_Status_f( void ) { int i; qboolean none; client_t *client; upstream_t *upstream; if( !TV_UpstreamForText( Cmd_Argv( 1 ), &upstream ) ) { Com_Printf( "No such connection\n" ); return; } if( upstream ) { Com_Printf( "%s" S_COLOR_WHITE ": %s\n", upstream->name, NET_AddressToString( &upstream->serveraddress ) ); Com_Printf( "Server name: %s\n", upstream->servername ); Com_Printf( "Connection: %s\n", TV_ConnstateToString( upstream->state ) ); Com_Printf( "Relay: %s\n", TV_ConnstateToString( upstream->relay.state ) ); } else { Com_Printf( "lobby\n" ); } Com_Printf( "Downstream connections:\n" ); none = qtrue; for( i = 0; i < tv_maxclients->integer; i++ ) { client = &tvs.clients[i]; if( client->state == CS_FREE || client->state == CS_ZOMBIE ) continue; if( !client->relay ) { if( upstream ) continue; } else { if( client->relay->upstream != upstream ) continue; } Com_Printf( "%3i: %s" S_COLOR_WHITE " (%s)\n", i, client->name, NET_AddressToString( &client->netchan.remoteAddress ) ); none = qfalse; } if( none ) Com_Printf( "- No downstream connections\n" ); }
qboolean CL_MM_Connect( const netadr_t *address ) { /* * ch : this here sends a ticket request to MM. * upon response we can set the client state to CA_CONNECTING * and actually connect to the gameserver */ stat_query_t *query; // Com_Printf("CL_MM_Connect %s\n", NET_AddressToString( address ) ); cls.mm_ticket = 0; // TODO: if not logged in, force login if( !cl_mm_enabled ) return qfalse; // TODO: validate the parameters query = sq_api->CreateQuery( "ccc", qfalse ); if( query == NULL ) return qfalse; // TODO: put the session in a cookie sq_api->SetField( query, "csession", va( "%d", cls.mm_session ) ); // we may have ipv4 or ipv6 in here, and MM currently on supports ipv4.. sq_api->SetField( query, "saddr", NET_AddressToString( address ) ); sq_api->SetCallback( query, cl_mm_connect_done, NULL ); sq_api->Send( query ); return qtrue; }
/* * SV_SendClientsFragments */ bool SV_SendClientsFragments( void ) { client_t *client; int i; bool sent = false; // send a message to each connected client for( i = 0, client = svs.clients; i < sv_maxclients->integer; i++, client++ ) { if( client->state == CS_FREE || client->state == CS_ZOMBIE ) continue; if( client->edict && ( client->edict->r.svflags & SVF_FAKECLIENT ) ) continue; if( !client->netchan.unsentFragments ) continue; if( !Netchan_TransmitNextFragment( &client->netchan ) ) { Com_Printf( "Error sending fragment to %s: %s\n", NET_AddressToString( &client->netchan.remoteAddress ), NET_ErrorString() ); if( client->reliable ) SV_DropClient( client, DROP_TYPE_GENERAL, "Error sending fragment: %s\n", NET_ErrorString() ); continue; } sent = true; } return sent; }
//================ // MM_MatchesList // Prints the list of matches to console //================ void MM_MatchList( void ) { int i = 0, count = 0; mm_match_t *match; for( match = mms.matches ; match ; match = match->next ) { Com_Printf( "Match id: %d\n ping type: %s\n gametype: %s\n skill type: %s\n clients:\n", match->id, match->pingtype == TYPE_ANY ? "any" : "dependent", match->gametype, match->skilltype == TYPE_ANY ? "any" : "dependent" ); for( i = 0; i < match->maxclients; i++ ) { if( match->clients[i] ) Com_Printf( " %s %s (%d): %s\n", match->clients[i]->userid < 1 ? "U" : "R", // registered or not match->clients[i]->nickname, match->clients[i]->userid, NET_AddressToString( &match->clients[i]->connection->address ) ); } count++; } Com_Printf( "%d match(s)\n", count ); }
/* * TV_Downstream_UpstreamlessPacket */ void TV_Downstream_UpstreamlessPacket( const socket_t *socket, const netadr_t *address, msg_t *msg ) { upstreamless_cmd_t *cmd; char *s, *c; MSG_BeginReading( msg ); MSG_ReadLong( msg ); // skip the -1 marker s = MSG_ReadStringLine( msg ); if( TV_Downstream_SteamServerQuery( s, socket, address, msg ) ) return; Cmd_TokenizeString( s ); c = Cmd_Argv( 0 ); for( cmd = upstreamless_cmds; cmd->name; cmd++ ) { if( !strcmp( c, cmd->name ) ) { cmd->func( socket, address ); return; } } Com_DPrintf( "Bad downstream connectionless packet from %s:\n%s\n", NET_AddressToString( address ), s ); }
/* * TV_Downstream_MasterHeartbeat * Send a message to the master every few minutes to * let it know we are alive, and log information */ void TV_Downstream_MasterHeartbeat( void ) { int i; const socket_t *socket; tvs.lobby.last_heartbeat -= tvs.lobby.snapFrameTime; if( tvs.lobby.last_heartbeat > 0 ) return; tvs.lobby.last_heartbeat = HEARTBEAT_SECONDS * 1000; if( !tv_public->integer ) return; // send to group master for( i = 0; i < MAX_MASTERS; i++ ) { if( tv_master_adr[i].type != NA_NOTRANSMIT ) { Com_Printf( "Sending heartbeat to %s\n", NET_AddressToString( &tv_master_adr[i] ) ); socket = ( tv_master_adr[i].type == NA_IP6 ? &tvs.socket_udp6 : &tvs.socket_udp ); // warning: "DarkPlaces" is a protocol name here, not a game name. Do not replace it. Netchan_OutOfBandPrint( socket, &tv_master_adr[i], "heartbeat %s\n", "DarkPlaces" ); } } }
/* * SV_MasterSendQuit * Notifies Steam master servers that the server is shutting down. */ void SV_MasterSendQuit( void ) { int i; const char quitMessage[] = "b\n"; if( !sv_public->integer || ( sv_maxclients->integer == 1 ) ) return; // never go public when not acting as a game server if( sv.state > ss_game ) return; // send to group master for( i = 0; i < MAX_MASTERS; i++ ) { sv_master_t *master = &sv_masters[i]; if( master->steam && ( master->address.type != NA_NOTRANSMIT ) ) { socket_t *socket = ( master->address.type == NA_IP6 ? &svs.socket_udp6 : &svs.socket_udp ); if( dedicated && dedicated->integer ) Com_Printf( "Sending quit to %s\n", NET_AddressToString( &master->address ) ); NET_SendPacket( socket, ( const uint8_t * )quitMessage, sizeof( quitMessage ), &master->address ); } } }
/* * CL_ParseStatusMessage * Handle a reply from a ping */ void CL_ParseStatusMessage( const socket_t *socket, const netadr_t *address, msg_t *msg ) { char *s = MSG_ReadString( msg ); serverlist_t *pingserver; char adrString[64]; Com_DPrintf( "%s\n", s ); Q_strncpyz( adrString, NET_AddressToString( address ), sizeof( adrString ) ); // ping response pingserver = CL_ServerFindInList( masterList, adrString ); if( !pingserver ) pingserver = CL_ServerFindInList( favoritesList, adrString ); if( pingserver && pingserver->pingTimeStamp ) // valid ping { unsigned int ping = Sys_Milliseconds() - pingserver->pingTimeStamp; CL_UIModule_AddToServerList( adrString, va( "\\\\ping\\\\%i%s", ping, s ) ); pingserver->pingTimeStamp = 0; pingserver->lastValidPing = Com_DaysSince1900(); return; } // assume LAN response if( NET_IsLANAddress( address ) && ( localQueryTimeStamp + LAN_SERVER_PINGING_TIMEOUT > Sys_Milliseconds() ) ) { unsigned int ping = Sys_Milliseconds() - localQueryTimeStamp; CL_UIModule_AddToServerList( adrString, va( "\\\\ping\\\\%i%s", ping, s ) ); return; } // add the server info, but ignore the ping, cause it's not valid CL_UIModule_AddToServerList( adrString, s ); }
/* ================== NET_SendPacket ================== */ void NET_SendPacket (netSrc_t sock, const netAdr_t to, const void *data, int length){ struct sockaddr adr; int result, error; if (NET_SendLoopPacket(sock, to, data, length)) return; if (to.type != NA_BROADCAST && to.type != NA_IP) Com_Error(ERR_FATAL, "NET_SendPacket: bad address type"); if (net.sockets[sock] == INVALID_SOCKET) return; NET_NetAdrToSockAdr(&to, &adr); result = sendto(net.sockets[sock], (const char *)data, length, 0, &adr, sizeof(adr)); if (result == SOCKET_ERROR){ error = WSAGetLastError(); // WSAEWOULDBLOCK is silent if (error == WSAEWOULDBLOCK) return; // Some PPP links don't allow broadcasts if (error == WSAEADDRNOTAVAIL && to.type == NA_BROADCAST) return; Com_Printf(S_COLOR_RED "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AddressToString(to)); } net.packetsSent[sock]++; net.bytesSent[sock] += result; }
/* * TV_Downstream_AddMaster_f * Add a master server to the list */ static void TV_Downstream_AddMaster_f( const char *master ) { int i; if( !master || !master[0] ) return; if( !tv_public->integer ) { Com_Printf( "'TV_Downstream_AddMaster_f' Only public servers use masters.\n" ); return; } for( i = 0; i < MAX_MASTERS; i++ ) { if( tv_master_adr[i].type != NA_NOTRANSMIT ) continue; if( !NET_StringToAddress( master, &tv_master_adr[i] ) ) { Com_Printf( "'SV_AddMaster_f' Bad Master server address: %s\n", master ); return; } if( NET_GetAddressPort( &tv_master_adr[i] ) == 0 ) NET_SetAddressPort( &tv_master_adr[i], PORT_MASTER ); Com_Printf( "Added new master server #%i at %s\n", i, NET_AddressToString( &tv_master_adr[i] ) ); return; } Com_Printf( "'TV_Downstream_AddMaster_f' List of master servers is already full\n" ); }
/* * SVC_GetChallenge * * Returns a challenge number that can be used * in a subsequent client_connect command. * We do this to prevent denial of service attacks that * flood the server with invalid connection IPs. With a * challenge, they must give a valid IP address. */ static void SVC_GetChallenge( const socket_t *socket, const netadr_t *address ) { int i; int oldest; int oldestTime; oldest = 0; oldestTime = 0x7fffffff; if( sv_showChallenge->integer ) Com_Printf( "Challenge Packet %s\n", NET_AddressToString( address ) ); // see if we already have a challenge for this ip for( i = 0; i < MAX_CHALLENGES; i++ ) { if( NET_CompareBaseAddress( address, &svs.challenges[i].adr ) ) break; if( svs.challenges[i].time < oldestTime ) { oldestTime = svs.challenges[i].time; oldest = i; } } if( i == MAX_CHALLENGES ) { // overwrite the oldest svs.challenges[oldest].challenge = rand() & 0x7fff; svs.challenges[oldest].adr = *address; svs.challenges[oldest].time = Sys_Milliseconds(); i = oldest; } Netchan_OutOfBandPrint( socket, address, "challenge %i", svs.challenges[i].challenge ); }
/* * SV_MasterHeartbeat * Send a message to the master every few minutes to * let it know we are alive, and log information */ void SV_MasterHeartbeat( void ) { int i; svc.lastHeartbeat -= svc.snapFrameTime; if( svc.lastHeartbeat > 0 ) return; svc.lastHeartbeat = HEARTBEAT_SECONDS * 1000; if( !sv_public->integer ) return; // never go public when not acting as a game server if( sv.state > ss_game ) return; // send to group master for( i = 0; i < MAX_MASTERS; i++ ) { if( master_adr[i].type != NA_NOTRANSMIT ) { socket_t *socket; if( dedicated && dedicated->integer ) Com_Printf( "Sending heartbeat to %s\n", NET_AddressToString( &master_adr[i] ) ); socket = ( master_adr[i].type == NA_IP6 ? &svs.socket_udp6 : &svs.socket_udp ); // warning: "DarkPlaces" is a protocol name here, not a game name. Do not replace it. Netchan_OutOfBandPrint( socket, &master_adr[i], "heartbeat DarkPlaces\n" ); } } }
/* * CL_Mumble_Update */ void CL_Mumble_Update( const vec3_t origin, const vec3_t forward, const vec3_t right, const vec3_t up, const char *identity ) { vec3_t mp, mf, mt; char context[256]; if( !cl_mumble->integer ) return; if( !identity ) return; VectorScale( origin, cl_mumble_scale->value, mp ); VectorSet( mf, forward[0], forward[2], forward[1] ); VectorSet( mt, up[0], up[2], up[1] ); if( cl_mumble->integer == 2 ) Com_Printf( "MumbleUpdate:\n%f, %f, %f\n%f, %f, %f\n%f, %f, %f", mp[0], mp[1], mp[2], mf[0], mf[1], mf[2], mt[0], mt[1], mt[2] ); mumble_update_coordinates( mp, mf, mt ); // for Mumble 1.2+ http://mumble.sourceforge.net/Link mumble_set_identity( identity ); // TODO: add team to context? Q_strncpyz( context, NET_AddressToString( &cls.serveraddress ), sizeof( context ) ); mumble_set_context( ( const unsigned char * )context, strlen( context ) + 1 ); }
/* * SVC_SendInfoString */ static void SVC_SendInfoString( const socket_t *socket, const netadr_t *address, const char *requestType, const char *responseType, qboolean fullStatus ) { char *string; if( sv_showInfoQueries->integer ) Com_Printf( "%s Packet %s\n", requestType, NET_AddressToString( address ) ); // KoFFiE: When not public and coming from a LAN address // assume broadcast and respond anyway, otherwise ignore if( ( ( !sv_public->integer ) && ( !NET_IsLANAddress( address ) ) ) || ( sv_maxclients->integer == 1 ) ) { return; } // ignore when in invalid server state if( sv.state < ss_loading || sv.state > ss_game ) return; // don't reply when we are locked for mm // if( SV_MM_IsLocked() ) // return; // send the same string that we would give for a status OOB command string = SV_LongInfoString( fullStatus ); if( string ) Netchan_OutOfBandPrint( socket, address, "%s\n\\challenge\\%s%s", responseType, Cmd_Argv( 1 ), string ); }
int SV_MM_ClientConnect( const netadr_t *address, char *userinfo, unsigned int ticket_id, int session_id ) { /* * what crizis did.. push a query after checking that ticket id and session id * at least aren't null and if server expects login-only clients * * ahem, figure out how to handle anonymous players. currently this will bug out so that * session_id is 0 -> request receives zero id's and freaks out. generate a session_id * here and return it. * * ok done. so this function receives session_id = 0 if we are dealing with 'anonymous' * player and this here generates local session-id for the client */ stat_query_t *query; // return of -1 is not an error, it just marks a dummy local session if( !sv_mm_initialized || !sv_mm_session ) return -1; // accept only players that are logged in (session_id <= 0 ??) if( sv_mm_loginonly->integer && session_id == 0 ) { Com_Printf("SV_MM_ClientConnect: Login-only\n"); return 0; } // expect a ticket for logged-in client (rly?) session_id > 0 // we should force local session in here if( ticket_id == 0 && session_id != 0 ) { Com_Printf( "SV_MM_ClientConnect: Logged-in client didnt declare ticket, marking as anonymous\n" ); session_id = 0; } if( session_id == 0 ) { // WMM doesnt care about anonymous players session_id = SV_MM_GenerateLocalSession(); Com_Printf("SV_MM_ClientConnect: Generated local session %d\n", session_id ); return session_id; } // push a request query = sq_api->CreateQuery( "scc", qfalse ); if( query == NULL ) return 0; // servers own session (TODO: put this to a cookie or smth) sq_api->SetField( query, "ssession", va("%d", sv_mm_session ) ); // clients attributes (nickname here?) sq_api->SetField( query, "cticket", va("%u", ticket_id ) ); sq_api->SetField( query, "csession", va("%d", session_id ) ); sq_api->SetField( query, "cip", NET_AddressToString( address ) ); sq_api->SetCallback( query, sv_mm_clientconnect_done, (void*)session_id ); sq_api->Send( query ); return session_id; }
/* * SVC_RemoteCommand * * A client issued an rcon command. * Shift down the remaining args * Redirect all printfs */ static void SVC_RemoteCommand( const socket_t *socket, const netadr_t *address ) { int i; char remaining[1024]; flush_params_t extra; i = Rcon_Validate(); if( i == 0 ) Com_Printf( "Bad rcon from %s:\n%s\n", NET_AddressToString( address ), Cmd_Args() ); else Com_Printf( "Rcon from %s:\n%s\n", NET_AddressToString( address ), Cmd_Args() ); extra.socket = socket; extra.address = address; Com_BeginRedirect( RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect, ( const void * )&extra ); if( sv_showRcon->integer ) Com_Printf( "Rcon Packet %s\n", NET_AddressToString( address ) ); if( !Rcon_Validate() ) { Com_Printf( "Bad rcon_password.\n" ); } else { remaining[0] = 0; for( i = 2; i < Cmd_Argc(); i++ ) { Q_strncatz( remaining, "\"", sizeof( remaining ) ); Q_strncatz( remaining, Cmd_Argv( i ), sizeof( remaining ) ); Q_strncatz( remaining, "\" ", sizeof( remaining ) ); } Cmd_ExecuteString( remaining ); } Com_EndRedirect(); }
/* ================== NET_GetPacket ================== */ bool NET_GetPacket (netSrc_t sock, netAdr_t *from, msg_t *msg){ struct sockaddr adr; int adrLen = sizeof(adr); int result, error; if (NET_GetLoopPacket(sock, from, msg)) return true; if (net.sockets[sock] == INVALID_SOCKET) return false; result = recvfrom(net.sockets[sock], (char *)msg->data, msg->maxSize, 0, (struct sockaddr *)&adr, &adrLen); NET_SockAdrToNetAdr(&adr, from); if (result == SOCKET_ERROR){ error = WSAGetLastError(); // WSAEWOULDBLOCK and WSAECONNRESET are silent if (error == WSAEWOULDBLOCK || error == WSAECONNRESET) return false; Com_Printf(S_COLOR_RED "NET_GetPacket: %s from %s\n", NET_ErrorString(), NET_AddressToString(*from)); return false; } if (result == msg->maxSize){ Com_Printf(S_COLOR_RED "NET_GetPacket: oversize packet from %s\n", NET_AddressToString(*from)); return false; } msg->curSize = result; net.packetsReceived[sock]++; net.bytesReceived[sock] += result; return true; }
/* * SV_MasterHeartbeat * Send a message to the master every few minutes to * let it know we are alive, and log information */ void SV_MasterHeartbeat( void ) { unsigned int time = Sys_Milliseconds(); int i; if( svc.nextHeartbeat > time ) return; svc.nextHeartbeat = time + HEARTBEAT_SECONDS * 1000; if( !sv_public->integer || ( sv_maxclients->integer == 1 ) ) return; // never go public when not acting as a game server if( sv.state > ss_game ) return; // send to group master for( i = 0; i < MAX_MASTERS; i++ ) { sv_master_t *master = &sv_masters[i]; if( master->address.type != NA_NOTRANSMIT ) { socket_t *socket; if( dedicated && dedicated->integer ) Com_Printf( "Sending heartbeat to %s\n", NET_AddressToString( &master->address ) ); socket = ( master->address.type == NA_IP6 ? &svs.socket_udp6 : &svs.socket_udp ); if( master->steam ) { uint8_t steamHeartbeat = 'q'; NET_SendPacket( socket, &steamHeartbeat, sizeof( steamHeartbeat ), &master->address ); } else { // warning: "DarkPlaces" is a protocol name here, not a game name. Do not replace it. Netchan_OutOfBandPrint( socket, &master->address, "heartbeat DarkPlaces\n" ); } } } }
/* * SV_AddMaster_f * Add a master server to the list */ static void SV_AddMaster_f( char *address, bool steam ) { int i; if( !address || !address[0] ) return; if( !sv_public->integer ) { Com_Printf( "'SV_AddMaster_f' Only public servers use masters.\n" ); return; } //never go public when not acting as a game server if( sv.state > ss_game ) return; for( i = 0; i < MAX_MASTERS; i++ ) { sv_master_t *master = &sv_masters[i]; if( master->address.type != NA_NOTRANSMIT ) continue; if( !NET_StringToAddress( address, &master->address ) ) { Com_Printf( "'SV_AddMaster_f' Bad Master server address: %s\n", address ); return; } if( NET_GetAddressPort( &master->address ) == 0 ) NET_SetAddressPort( &master->address, steam ? PORT_MASTER_STEAM : PORT_MASTER ); master->steam = steam; Com_Printf( "Added new master server #%i at %s\n", i, NET_AddressToString( &master->address ) ); return; } Com_Printf( "'SV_AddMaster_f' List of master servers is already full\n" ); }
/* * 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; } }
/* * SV_AddMaster_f * Add a master server to the list */ static void SV_AddMaster_f( char *master ) { int i; if( !master || !master[0] ) return; if( !sv_public->integer ) { Com_Printf( "'SV_AddMaster_f' Only public servers use masters.\n" ); return; } //never go public when not acting as a game server if( sv.state > ss_game ) return; for( i = 0; i < MAX_MASTERS; i++ ) { if( master_adr[i].type != NA_NOTRANSMIT ) continue; if( !NET_StringToAddress( master, &master_adr[i] ) ) { Com_Printf( "'SV_AddMaster_f' Bad Master server address: %s\n", master ); return; } if( NET_GetAddressPort( &master_adr[i] ) == 0 ) NET_SetAddressPort( &master_adr[i], PORT_MASTER ); Com_Printf( "Added new master server #%i at %s\n", i, NET_AddressToString( &master_adr[i] ) ); return; } Com_Printf( "'SV_AddMaster_f' List of master servers is already full\n" ); }
/* * TV_Status */ static void TV_Status( void ) { int i; qboolean none; client_t *client; Com_Printf( "Upstream connections:\n" ); none = qtrue; for( i = 0; i < tvs.numupstreams; i++ ) { if( !tvs.upstreams[i] ) continue; Com_Printf( "%3i: %22s: %s\n", i+1, NET_AddressToString( &tvs.upstreams[i]->serveraddress ), tvs.upstreams[i]->name ); none = qfalse; } if( none ) Com_Printf( "- No upstream connections\n" ); Com_Printf( "Downstream connections:\n" ); none = qtrue; for( i = 0; i < tv_maxclients->integer; i++ ) { client = &tvs.clients[i]; if( client->state == CS_FREE || client->state == CS_ZOMBIE ) continue; Com_Printf( "%3i: %s" S_COLOR_WHITE " (%s): %s" S_COLOR_WHITE " %s\n", i, client->name, NET_AddressToString( &client->netchan.remoteAddress ), ( client->relay ? client->relay->upstream->name : "lobby" ), client->mv ? "MV" : "" ); none = qfalse; } if( none ) Com_Printf( "- No downstream connections\n" ); }
/* * CL_GetServers_f */ void CL_GetServers_f( void ) { netadr_t adr, *padr; char *requeststring; int i; char *modname, *master; trie_error_t err; filter_allow_full = qfalse; filter_allow_empty = qfalse; for( i = 2; i < Cmd_Argc(); i++ ) { if( !Q_stricmp( "full", Cmd_Argv( i ) ) ) filter_allow_full = qtrue; if( !Q_stricmp( "empty", Cmd_Argv( i ) ) ) filter_allow_empty = qtrue; } if( !Q_stricmp( Cmd_Argv( 1 ), "local" ) ) { if( localQueryTimeStamp + LAN_SERVER_PINGING_TIMEOUT > Sys_Milliseconds() ) { return; } padr = &adr; localQueryTimeStamp = Sys_Milliseconds(); // send a broadcast packet Com_DPrintf( "pinging broadcast...\n" ); // erm... modname isn't sent in local queries? requeststring = va( "info %i %s %s", SERVERBROWSER_PROTOCOL_VERSION, filter_allow_full ? "full" : "", filter_allow_empty ? "empty" : "" ); for( i = 0; i < NUM_BROADCAST_PORTS; i++ ) { NET_BroadcastAddress( padr, PORT_SERVER + i ); Netchan_OutOfBandPrint( &cls.socket_udp, padr, requeststring ); } return; } //get what master master = Cmd_Argv( 2 ); if( !master || !( *master ) ) return; modname = Cmd_Argv( 3 ); // never allow anyone to use DEFAULT_BASEGAME as mod name if( !modname || !modname[0] || !Q_stricmp( modname, DEFAULT_BASEGAME ) ) modname = APPLICATION; assert( modname[0] ); // check memory cache QMutex_Lock( resolveLock ); err = Trie_Find( serverlist_masters_trie, master, TRIE_EXACT_MATCH, (void **)&padr ); QMutex_Unlock( resolveLock ); if( err == TRIE_OK && ( padr->type == NA_IP || padr->type == NA_IP6 ) ) { const char *cmdname; socket_t *socket; if ( padr->type == NA_IP ) { cmdname = "getservers"; socket = &cls.socket_udp; } else { cmdname = "getserversExt"; socket = &cls.socket_udp6; } // create the message requeststring = va( "%s %c%s %i %s %s", cmdname, toupper( modname[0] ), modname+1, SERVERBROWSER_PROTOCOL_VERSION, filter_allow_full ? "full" : "", filter_allow_empty ? "empty" : "" ); if( NET_GetAddressPort( padr ) == 0 ) NET_SetAddressPort( padr, PORT_MASTER ); Netchan_OutOfBandPrint( socket, padr, requeststring ); Com_DPrintf( "quering %s...%s: %s\n", master, NET_AddressToString(padr), requeststring ); } else { Com_Printf( "Bad address: %s\n", master ); } }
/* * SV_Status_f */ void SV_Status_f( void ) { int i, j, l; client_t *cl; const char *s; int ping; if( !svs.clients ) { Com_Printf( "No server running.\n" ); return; } Com_Printf( "map : %s\n", sv.mapname ); Com_Printf( "num score ping name lastmsg address port rate \n" ); Com_Printf( "--- ----- ---- --------------- ------- --------------------- ------ ------\n" ); for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( !cl->state ) continue; Com_Printf( "%3i ", i ); Com_Printf( "%5i ", cl->edict->r.client->r.frags ); if( cl->state == CS_CONNECTED ) Com_Printf( "CNCT " ); else if( cl->state == CS_ZOMBIE ) Com_Printf( "ZMBI " ); else if( cl->state == CS_CONNECTING ) Com_Printf( "AWAI " ); else { ping = cl->ping < 9999 ? cl->ping : 9999; Com_Printf( "%4i ", ping ); } s = COM_RemoveColorTokens( cl->name ); Com_Printf( "%s", s ); l = 16 - (int)strlen( s ); for( j = 0; j < l; j++ ) Com_Printf( " " ); Com_Printf( "%7i ", svs.realtime - cl->lastPacketReceivedTime ); s = NET_AddressToString( &cl->netchan.remoteAddress ); Com_Printf( "%s", s ); l = 22 - (int)strlen( s ); for( j = 0; j < l; j++ ) Com_Printf( " " ); Com_Printf( "%5i", cl->netchan.game_port ); #ifndef RATEKILLED // wsw : jal : print real rate in use Com_Printf( " " ); if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) Com_Printf( "BOT" ); else if( cl->rate == 99999 ) Com_Printf( "LAN" ); else Com_Printf( "%5i", cl->rate ); #endif Com_Printf( " " ); if( cl->mv ) Com_Printf( "MV" ); Com_Printf( "\n" ); } Com_Printf( "\n" ); }
/* * Netchan_Process * * Returns false if the message should not be processed due to being * out of order or a fragment. * * Msg must be large enough to hold MAX_MSGLEN, because if this is the * final fragment of a multi-part message, the entire thing will be * copied out. */ bool Netchan_Process( netchan_t *chan, msg_t *msg ) { int sequence, sequence_ack; int game_port = -1; int fragmentStart, fragmentLength; bool fragmented = false; int headerlength; bool compressed = false; bool lastfragment = false; // get sequence numbers MSG_BeginReading( msg ); sequence = MSG_ReadLong( msg ); sequence_ack = MSG_ReadLong( msg ); // wsw : jal : by now our header sends incoming ack too (q3 doesn't) // check for fragment information if( sequence & FRAGMENT_BIT ) { sequence &= ~FRAGMENT_BIT; fragmented = true; if( net_showfragments->integer ) Com_Printf( "Process fragmented packet (%s) (id:%i)\n", NET_SocketToString( chan->socket ), sequence ); } else { fragmented = false; } // wsw : jal : check for compressed information if( sequence_ack & FRAGMENT_BIT ) { sequence_ack &= ~FRAGMENT_BIT; compressed = true; if( !fragmented ) msg->compressed = true; } // read the game port if we are a server if( chan->socket->server ) game_port = MSG_ReadShort( msg ); // read the fragment information if( fragmented ) { fragmentStart = MSG_ReadShort( msg ); fragmentLength = MSG_ReadShort( msg ); if( fragmentLength & FRAGMENT_LAST ) { lastfragment = true; fragmentLength &= ~FRAGMENT_LAST; } } else { fragmentStart = 0; // stop warning message fragmentLength = 0; } if( showpackets->integer ) { if( fragmented ) { Com_Printf( "%s recv %4i : s=%i fragment=%i,%i\n", NET_SocketToString( chan->socket ), msg->cursize, sequence, fragmentStart, fragmentLength ); } else { Com_Printf( "%s recv %4i : s=%i\n", NET_SocketToString( chan->socket ), msg->cursize, sequence ); } } // // discard out of order or duplicated packets // if( sequence <= chan->incomingSequence ) { if( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Out of order packet %i at %i\n", NET_AddressToString( &chan->remoteAddress ), sequence, chan->incomingSequence ); } return false; } // // dropped packets don't keep the message from being used // chan->dropped = sequence - ( chan->incomingSequence+1 ); if( chan->dropped > 0 ) { if( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Dropped %i packets at %i\n", NET_AddressToString( &chan->remoteAddress ), chan->dropped, sequence ); } } // // if this is the final framgent of a reliable message, // bump incoming_reliable_sequence // if( fragmented ) { // TTimo // make sure we add the fragments in correct order // either a packet was dropped, or we received this one too soon // we don't reconstruct the fragments. we will wait till this fragment gets to us again // (NOTE: we could probably try to rebuild by out of order chunks if needed) if( sequence != chan->fragmentSequence ) { chan->fragmentSequence = sequence; chan->fragmentLength = 0; } // if we missed a fragment, dump the message if( fragmentStart != (int) chan->fragmentLength ) { if( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Dropped a message fragment\n", NET_AddressToString( &chan->remoteAddress ), sequence ); } // we can still keep the part that we have so far, // so we don't need to clear chan->fragmentLength return false; } // copy the fragment to the fragment buffer if( fragmentLength < 0 || msg->readcount + fragmentLength > msg->cursize || chan->fragmentLength + fragmentLength > sizeof( chan->fragmentBuffer ) ) { if( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:illegal fragment length\n", NET_AddressToString( &chan->remoteAddress ) ); } return false; } memcpy( chan->fragmentBuffer + chan->fragmentLength, msg->data + msg->readcount, fragmentLength ); chan->fragmentLength += fragmentLength; // if this wasn't the last fragment, don't process anything if( !lastfragment ) { return false; } if( chan->fragmentLength > msg->maxsize ) { Com_Printf( "%s:fragmentLength %i > msg->maxsize\n", NET_AddressToString( &chan->remoteAddress ), chan->fragmentLength ); return false; } // wsw : jal : reconstruct the message MSG_Clear( msg ); MSG_WriteLong( msg, sequence ); MSG_WriteLong( msg, sequence_ack ); if( chan->socket->server ) MSG_WriteShort( msg, game_port ); msg->compressed = compressed; headerlength = msg->cursize; MSG_CopyData( msg, chan->fragmentBuffer, chan->fragmentLength ); msg->readcount = headerlength; // put read pointer after header again chan->fragmentLength = 0; //let it be finished as standard packets } // the message can now be read from the current message pointer chan->incomingSequence = sequence; // wsw : jal[start] : get the ack from the very first fragment chan->incoming_acknowledged = sequence_ack; // wsw : jal[end] return true; }
/* * SVC_Ack */ static void SVC_Ack( const socket_t *socket, const netadr_t *address ) { Com_Printf( "Ping acknowledge from %s\n", NET_AddressToString( address ) ); }
/* * SVC_DirectConnect * A connection request that did not come from the master */ static void SVC_DirectConnect( const socket_t *socket, const netadr_t *address ) { #ifdef TCP_ALLOW_CONNECT int incoming = 0; #endif char userinfo[MAX_INFO_STRING]; client_t *cl, *newcl; int i, version, game_port, challenge; int previousclients; int session_id; char *session_id_str; unsigned int ticket_id; qboolean tv_client; Com_DPrintf( "SVC_DirectConnect (%s)\n", Cmd_Args() ); 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 ); } Com_DPrintf( " rejected connect from protocol %i\n", version ); return; } game_port = atoi( Cmd_Argv( 2 ) ); challenge = atoi( Cmd_Argv( 3 ) ); tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? qtrue : qfalse ); 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( "Connection 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( "Connection 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( "Connection from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) ); return; } if( Cmd_Argc() >= 7 ) { // we have extended information, ticket-id and session-id Com_Printf("Extended information %s\n", Cmd_Argv(6) ); ticket_id = (unsigned int)atoi( Cmd_Argv(6) ); session_id_str = Info_ValueForKey( userinfo, "cl_mm_session" ); if( session_id_str != NULL ) session_id = atoi( session_id_str ); else session_id = 0; } else { ticket_id = 0; session_id = 0; } #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { // find the connection for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) continue; if( NET_CompareAddress( &svs.incoming[i].address, address ) && socket == &svs.incoming[i].socket ) break; } if( i == MAX_INCOMING_CONNECTIONS ) { Com_Error( ERR_FATAL, "Incoming connection not found.\n" ); return; } incoming = i; } #endif // see if the challenge is valid for( i = 0; i < MAX_CHALLENGES; i++ ) { if( NET_CompareBaseAddress( address, &svs.challenges[i].adr ) ) { if( challenge == svs.challenges[i].challenge ) { svs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge svs.challenges[i].time = 0; NET_InitAddress( &svs.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; } //r1: limit connections from a single IP if( sv_iplimit->integer ) { previousclients = 0; for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) ) { //r1: zombies are less dangerous if( cl->state == CS_ZOMBIE ) previousclients++; else previousclients += 2; } } if( previousclients >= sv_iplimit->integer * 2 ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nToo many connections from your host\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "%s:connect rejected : too many connections\n", NET_AddressToString( address ) ); return; } } newcl = NULL; // if there is already a slot for this ip, reuse it for( i = 0, cl = svs.clients; i < sv_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 ) && ( svs.realtime - cl->lastconnect ) < (unsigned)( sv_reconnectlimit->integer * 1000 ) ) { Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AddressToString( address ) ); return; } Com_Printf( "%s:reconnect\n", NET_AddressToString( address ) ); newcl = cl; break; } } // find a client slot if( !newcl ) { for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) { newcl = cl; break; } // overwrite fakeclient if no free spots found if( cl->state && cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) newcl = cl; } if( !newcl ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "Server is full. Rejected a connection.\n" ); return; } if( newcl->state && newcl->edict && ( newcl->edict->r.svflags & SVF_FAKECLIENT ) ) SV_DropClient( newcl, DROP_TYPE_GENERAL, "Need room for a real player" ); } // get the game a chance to reject this connection or modify the userinfo if( !SV_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, qfalse, tv_client, ticket_id, session_id ) ) { char *rejtype, *rejflag, *rejtypeflag, *rejmsg; rejtype = Info_ValueForKey( userinfo, "rejtype" ); if( !rejtype ) rejtype = "0"; rejflag = Info_ValueForKey( userinfo, "rejflag" ); if( !rejflag ) rejflag = "0"; // hax because Info_ValueForKey can only be called twice in a row rejtypeflag = va( "%s\n%s", rejtype, rejflag ); rejmsg = Info_ValueForKey( userinfo, "rejmsg" ); if( !rejmsg ) rejmsg = "Game module rejected connection"; Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg ); Com_DPrintf( "Game rejected a connection.\n" ); return; } // send the connect packet to the client Netchan_OutOfBandPrint( socket, address, "client_connect\n%s", newcl->session ); // free the incoming entry #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { svs.incoming[incoming].active = qfalse; svs.incoming[incoming].socket.open = qfalse; } #endif }
/* * SVC_InfoResponse * * Responds with short info for broadcast scans * The second parameter should be the current protocol version number. */ static void SVC_InfoResponse( const socket_t *socket, const netadr_t *address ) { int i, count; char *string; qboolean allow_empty = qfalse, allow_full = qfalse; if( sv_showInfoQueries->integer ) Com_Printf( "Info Packet %s\n", NET_AddressToString( address ) ); // KoFFiE: When not public and coming from a LAN address // assume broadcast and respond anyway, otherwise ignore if( ( ( !sv_public->integer ) && ( !NET_IsLANAddress( address ) ) ) || ( sv_maxclients->integer == 1 ) ) { return; } // ignore when in invalid server state if( sv.state < ss_loading || sv.state > ss_game ) return; // don't reply when we are locked for mm // if( SV_MM_IsLocked() ) // return; // different protocol version if( atoi( Cmd_Argv( 1 ) ) != APP_PROTOCOL_VERSION ) return; // check for full/empty filtered states for( i = 0; i < Cmd_Argc(); i++ ) { if( !Q_stricmp( Cmd_Argv( i ), "full" ) ) allow_full = qtrue; if( !Q_stricmp( Cmd_Argv( i ), "empty" ) ) allow_empty = qtrue; } count = 0; for( i = 0; i < sv_maxclients->integer; i++ ) { if( svs.clients[i].state >= CS_CONNECTED ) { count++; } } if( ( count == sv_maxclients->integer ) && !allow_full ) { return; } if( ( count == 0 ) && !allow_empty ) { return; } string = SV_ShortInfoString(); if( string ) Netchan_OutOfBandPrint( socket, address, "info\n%s", string ); }