/* * 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" ); }
/* * CL_QueryGetInfoMessage */ static void CL_QueryGetInfoMessage( const char *cmdname ) { netadr_t adr; char *requeststring; char *server; //get what master server = Cmd_Argv( 1 ); if( !server || !( *server ) ) { Com_Printf( "%s: no address provided %s...\n", Cmd_Argv( 0 ), server ? server : "" ); return; } requeststring = va( cmdname ); // send a broadcast packet Com_DPrintf( "quering %s...\n", server ); if( NET_StringToAddress( server, &adr ) ) { socket_t *socket; if( NET_GetAddressPort( &adr ) == 0 ) NET_SetAddressPort( &adr, PORT_SERVER ); socket = ( adr.type == NA_IP6 ? &cls.socket_udp6 : &cls.socket_udp ); Netchan_OutOfBandPrint( socket, &adr, requeststring ); } else { Com_Printf( "Bad address: %s\n", server ); } }
/* * 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" ); }
/* * 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" ); }
/* * 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 ); } }
/* * NET_IP_OpenSocket */ static qboolean NET_IP_OpenSocket( socket_t *sock, const netadr_t *address, socket_type_t socktype, qboolean server ) { int newsocket; const char *proto, *stype; assert( sock && !sock->open ); assert( address ); if( address->type == NA_IP ) proto = "IP"; else if( address->type == NA_IP6 ) proto = "IPv6"; else { NET_SetErrorString( "Invalid address type" ); return qfalse; } if( socktype == SOCKET_UDP ) stype = "UDP"; #ifdef TCP_SUPPORT else if( socktype == SOCKET_TCP ) stype = "TCP"; #endif else { NET_SetErrorString( "Invalid socket type" ); return qfalse; } if( NET_IsAnyAddress( address ) ) { Com_Printf( "Opening %s/%s socket: *:%hu\n", stype, proto, NET_GetAddressPort( address ) ); } else { Com_Printf( "Opening %s/%s socket: %s\n", stype, proto, NET_AddressToString( address ) ); } if( ( newsocket = OpenSocket( socktype, ( address->type == NA_IP6 ? qtrue : qfalse ) ) ) == INVALID_SOCKET ) return qfalse; // make it non-blocking if( !NET_SocketMakeNonBlocking( newsocket ) ) { Sys_NET_SocketClose( newsocket ); return qfalse; } if( socktype == SOCKET_UDP ) { // make it broadcast capable if( !NET_SocketMakeBroadcastCapable( newsocket ) ) { Sys_NET_SocketClose( newsocket ); return qfalse; } } // wsw : pb : make it reusable (fast release of port when quit) /*if( setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)) == -1 ) { SetErrorStringFromErrno( "setsockopt" ); return 0; }*/ if( !BindSocket( newsocket, address ) ) { Sys_NET_SocketClose( newsocket ); return qfalse; } sock->open = qtrue; sock->type = socktype; sock->address = *address; sock->server = server; sock->handle = newsocket; return qtrue; }
/* * CL_ParseServerData */ static void CL_ParseServerData( msg_t *msg ) { const char *str, *gamedir; int i, sv_bitflags, numpure; int http_portnum; Com_DPrintf( "Serverdata packet received.\n" ); // wipe the client_state_t struct CL_ClearState(); CL_SetClientState( CA_CONNECTED ); // parse protocol version number i = MSG_ReadLong( msg ); if( i != APP_PROTOCOL_VERSION && !(cls.demo.playing && i == APP_DEMO_PROTOCOL_VERSION) ) Com_Error( ERR_DROP, "Server returned version %i, not %i", i, APP_PROTOCOL_VERSION ); cl.servercount = MSG_ReadLong( msg ); cl.snapFrameTime = (unsigned int)MSG_ReadShort( msg ); cl.gamestart = true; // set extrapolation time to half snapshot time Cvar_ForceSet( "cl_extrapolationTime", va( "%i", (unsigned int)( cl.snapFrameTime * 0.5 ) ) ); cl_extrapolationTime->modified = false; // base game directory str = MSG_ReadString( msg ); if( !str || !str[0] ) Com_Error( ERR_DROP, "Server sent an empty base game directory" ); if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) ) Com_Error( ERR_DROP, "Server sent an invalid base game directory: %s", str ); if( strcmp( FS_BaseGameDirectory(), str ) ) { Com_Error( ERR_DROP, "Server has different base game directory (%s) than the client (%s)", str, FS_BaseGameDirectory() ); } // game directory str = MSG_ReadString( msg ); if( !str || !str[0] ) Com_Error( ERR_DROP, "Server sent an empty game directory" ); if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) ) Com_Error( ERR_DROP, "Server sent an invalid game directory: %s", str ); gamedir = FS_GameDirectory(); if( strcmp( str, gamedir ) ) { // shutdown the cgame module first in case it is running for whatever reason // (happens on wswtv in lobby), otherwise precaches that are going to follow // will probably f**k up (like models trying to load before the world model) CL_GameModule_Shutdown(); if( !FS_SetGameDirectory( str, true ) ) Com_Error( ERR_DROP, "Failed to load game directory set by server: %s", str ); ML_Restart( true ); } // parse player entity number cl.playernum = MSG_ReadShort( msg ); // get the full level name Q_strncpyz( cl.servermessage, MSG_ReadString( msg ), sizeof( cl.servermessage ) ); sv_bitflags = MSG_ReadByte( msg ); if( cls.demo.playing ) { cls.reliable = ( sv_bitflags & SV_BITFLAGS_RELIABLE ); } else { if( cls.reliable != ( ( sv_bitflags & SV_BITFLAGS_RELIABLE ) != 0 ) ) Com_Error( ERR_DROP, "Server and client disagree about connection reliability" ); } // builting HTTP server port if( cls.httpbaseurl ) { Mem_Free( cls.httpbaseurl ); cls.httpbaseurl = NULL; } if( ( sv_bitflags & SV_BITFLAGS_HTTP ) != 0 ) { if( ( sv_bitflags & SV_BITFLAGS_HTTP_BASEURL ) != 0 ) { // read base upstream url cls.httpbaseurl = ZoneCopyString( MSG_ReadString( msg ) ); } else { http_portnum = MSG_ReadShort( msg ) & 0xffff; cls.httpaddress = cls.serveraddress; if( cls.httpaddress.type == NA_IP6 ) { cls.httpaddress.address.ipv6.port = BigShort( http_portnum ); } else { cls.httpaddress.address.ipv4.port = BigShort( http_portnum ); } if( http_portnum ) { if( cls.httpaddress.type == NA_LOOPBACK ) { cls.httpbaseurl = ZoneCopyString( va( "http://localhost:%hu/", http_portnum ) ); } else { cls.httpbaseurl = ZoneCopyString( va( "http://%s/", NET_AddressToString( &cls.httpaddress ) ) ); } } } } // pure list // clean old, if necessary Com_FreePureList( &cls.purelist ); // add new numpure = MSG_ReadShort( msg ); while( numpure > 0 ) { const char *pakname = MSG_ReadString( msg ); const unsigned checksum = MSG_ReadLong( msg ); Com_AddPakToPureList( &cls.purelist, pakname, checksum, NULL ); numpure--; } //assert( numpure == 0 ); // get the configstrings request CL_AddReliableCommand( va( "configstrings %i 0", cl.servercount ) ); cls.sv_pure = ( sv_bitflags & SV_BITFLAGS_PURE ) != 0; cls.sv_tv = ( sv_bitflags & SV_BITFLAGS_TVSERVER ) != 0; #ifdef PURE_CHEAT cls.sv_pure = false; #endif cls.wakelock = Sys_AcquireWakeLock(); if( !cls.demo.playing && ( cls.serveraddress.type == NA_IP ) ) Steam_AdvertiseGame( cls.serveraddress.address.ipv4.ip, NET_GetAddressPort( &cls.serveraddress ) ); // separate the printfs so the server message can have a color Com_Printf( S_COLOR_WHITE "\n" "=====================================\n" ); Com_Printf( S_COLOR_WHITE "%s\n\n", cl.servermessage ); }
/* * SV_ReadPackets */ static void SV_ReadPackets( void ) { int i, socketind, ret; client_t *cl; #ifdef TCP_SUPPORT socket_t newsocket; netadr_t mmserver; #endif int game_port; socket_t *socket; netadr_t address; static msg_t msg; static qbyte msgData[MAX_MSGLEN]; socket_t* sockets [] = { &svs.socket_loopback, &svs.socket_udp, &svs.socket_udp6, }; #ifdef TCP_SUPPORT if( SV_MM_Initialized() ) SV_MM_NetAddress( &mmserver ); if( svs.socket_tcp.open ) { while( qtrue ) { // find a free slot for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) break; } if( i == MAX_INCOMING_CONNECTIONS ) break; if( ( ret = NET_Accept( &svs.socket_tcp, &newsocket, &address ) ) == 0 ) break; if( ret == -1 ) { Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() ); continue; } Com_Printf( "New TCP connection from %s\n", NET_AddressToString( &address ) ); svs.incoming[i].active = qtrue; svs.incoming[i].socket = newsocket; svs.incoming[i].address = address; svs.incoming[i].time = svs.realtime; } } for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) continue; ret = NET_GetPacket( &svs.incoming[i].socket, &address, &msg ); if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; } else if( ret == 1 ) { if( SV_MM_Initialized() && NET_CompareBaseAddress( &mmserver, &address ) ) { Com_DPrintf( "TCP packet from matchmaker\n" ); SV_MM_SetConnection( &svs.incoming[i] ); SV_MM_Packet( &msg ); SV_MM_SetConnection( NULL ); continue; } if( *(int *)msg.data != -1 ) { Com_Printf( "Sequence packet without connection\n" ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; continue; } Com_Printf( "Connectionless TCP packet from: %s\n", NET_AddressToString( &address ) ); SV_ConnectionlessPacket( &svs.incoming[i].socket, &address, &msg ); } } #endif MSG_Init( &msg, msgData, sizeof( msgData ) ); for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ ) { socket = sockets[socketind]; if( !socket->open ) continue; while( ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); continue; } // check for connectionless packet (0xffffffff) first if( *(int *)msg.data == -1 ) { SV_ConnectionlessPacket( socket, &address, &msg ); continue; } // read the game port out of the message so we can fix up // stupid address translating routers MSG_BeginReading( &msg ); MSG_ReadLong( &msg ); // sequence number MSG_ReadLong( &msg ); // sequence number game_port = MSG_ReadShort( &msg ) & 0xffff; // data follows // check for packets from connected clients for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { unsigned short addr_port; if( cl->state == CS_FREE || cl->state == CS_ZOMBIE ) continue; if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) continue; if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) ) continue; if( cl->netchan.game_port != game_port ) continue; addr_port = NET_GetAddressPort( &address ); if( NET_GetAddressPort( &cl->netchan.remoteAddress ) != addr_port ) { Com_Printf( "SV_ReadPackets: fixing up a translated port\n" ); NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port ); } if( SV_ProcessPacket( &cl->netchan, &msg ) ) // this is a valid, sequenced packet, so process it { cl->lastPacketReceivedTime = svs.realtime; SV_ParseClientMessage( cl, &msg ); } break; } } } // handle clients with individual sockets for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state == CS_ZOMBIE || cl->state == CS_FREE ) continue; if( !cl->individual_socket ) continue; // not while, we only handle one packet per client at a time here if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "Error receiving packet from %s: %s\n", NET_AddressToString( &cl->netchan.remoteAddress ), NET_ErrorString() ); if( cl->reliable ) SV_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() ); } else { if( SV_ProcessPacket( &cl->netchan, &msg ) ) { // this is a valid, sequenced packet, so process it cl->lastPacketReceivedTime = svs.realtime; SV_ParseClientMessage( cl, &msg ); } } } } }
/* * TV_Downstream_ReadPackets */ void TV_Downstream_ReadPackets( void ) { int i, socketind, ret, game_port; client_t *cl; #ifdef TCP_SUPPORT socket_t newsocket; #endif socket_t *socket; netadr_t address; msg_t msg; qbyte msgData[MAX_MSGLEN]; socket_t* sockets [] = { &tvs.socket_udp, &tvs.socket_udp6, }; #ifdef TCP_SUPPORT if( tvs.socket_tcp.open ) { while( qtrue ) { // find a free slot for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !tvs.incoming[i].active ) break; } if( i == MAX_INCOMING_CONNECTIONS ) break; if( ( ret = NET_Accept( &tvs.socket_tcp, &newsocket, &address ) ) == 0 ) break; if( ret == -1 ) { Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() ); continue; } tvs.incoming[i].active = qtrue; tvs.incoming[i].socket = newsocket; tvs.incoming[i].address = address; tvs.incoming[i].time = tvs.realtime; } } for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !tvs.incoming[i].active ) continue; ret = NET_GetPacket( &tvs.incoming[i].socket, &address, &msg ); if( ret == -1 ) { NET_CloseSocket( &tvs.incoming[i].socket ); tvs.incoming[i].active = qfalse; } else if( ret == 1 ) { if( *(int *)msg.data != -1 ) { // sequence packet without upstreams NET_CloseSocket( &tvs.incoming[i].socket ); tvs.incoming[i].active = qfalse; continue; } TV_Downstream_UpstreamlessPacket( &tvs.incoming[i].socket, &address, &msg ); } } #endif MSG_Init( &msg, msgData, sizeof( msgData ) ); for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ ) { socket = sockets[socketind]; while( socket->open && ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); continue; } // check for upstreamless packet (0xffffffff) first if( *(int *)msg.data == -1 ) { TV_Downstream_UpstreamlessPacket( socket, &address, &msg ); continue; } // read the game port out of the message so we can fix up // stupid address translating routers MSG_BeginReading( &msg ); MSG_ReadLong( &msg ); // sequence number MSG_ReadLong( &msg ); // sequence number game_port = MSG_ReadShort( &msg ) & 0xffff; // data follows // check for packets from connected clients for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ ) { unsigned short remoteaddr_port, addr_port; if( cl->state == CS_FREE || cl->state == CS_ZOMBIE ) continue; if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) ) continue; if( cl->netchan.game_port != game_port ) continue; remoteaddr_port = NET_GetAddressPort( &cl->netchan.remoteAddress ); addr_port = NET_GetAddressPort( &address ); if( remoteaddr_port != addr_port ) { Com_DPrintf( "%s" S_COLOR_WHITE ": Fixing up a translated port from %i to %i\n", cl->name, remoteaddr_port, addr_port ); NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port ); } if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) ) { // this is a valid, sequenced packet, so process it cl->lastPacketReceivedTime = tvs.realtime; TV_Downstream_ParseClientMessage( cl, &msg ); } break; } } } // handle clients with individual sockets for( i = 0; i < tv_maxclients->integer; i++ ) { cl = &tvs.clients[i]; if( cl->state == CS_ZOMBIE || cl->state == CS_FREE ) continue; if( !cl->individual_socket ) continue; // not while, we only handle one packet per client at a time here if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "%s" S_COLOR_WHITE ": Error receiving packet: %s\n", cl->name, NET_ErrorString() ); if( cl->reliable ) TV_Downstream_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() ); } else { if( *(int *)msg.data == -1 ) { TV_Downstream_UpstreamlessPacket( cl->netchan.socket, &address, &msg ); } else { if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) ) { cl->lastPacketReceivedTime = tvs.realtime; TV_Downstream_ParseClientMessage( cl, &msg ); } } } } } }