void SVC_QRY_ParseMasterReply(void) { int i, c; master_t *m; int ret = net_message.cursize; unsigned char *answer = net_message.data; // not the smartest way, but why copy from one place to another... // no point to parse it, we do not query masters if (!masters_query->integer) { Sys_DPrintf("master server reply ignored\n"); return; } Sys_DPrintf ("master server reply from %s:%d\n", inet_ntoa(net_from.sin_addr), (int)ntohs(net_from.sin_port)); // is it reply from registered master server or someone trying to do some evil things? for (i = 0, m = masters.master; i < MAX_MASTERS; i++, m++) { if (m->state != ms_used) continue; // master slot not used if (NET_CompareAddress(&net_from, &m->addr)) { // OK - it is reply from registered master server m->next_query = time(NULL) + QW_MASTER_QUERY_TIME; // delay next query for some time break; } } if (i >= MAX_MASTERS) { Sys_Printf("Reply from not registered master server\n"); return; } Sys_DPrintf("master server returned %d bytes\n", ret); for (c = 0, i = 6; i + 5 < ret; i += 6, c++) { char ip[64]; int port = 256 * (int)answer[i+4] + (int)answer[i+5]; snprintf(ip, sizeof(ip), "%u.%u.%u.%u", (int)answer[i+0], (int)answer[i+1], (int)answer[i+2], (int)answer[i+3]); if (developer->integer > 1) Sys_DPrintf("SERVER: %4d %s:%d\n", c, ip, port); QRY_SV_new(ip, port, true); } }
peer_t *FWD_peer_by_addr(struct sockaddr_in *from) { peer_t *p; for (p = peers; p; p = p->next) { if (NET_CompareAddress(&p->from, from)) return p; } return NULL; }
static master_t *QRY_Master_ByAddr(struct sockaddr_in *addr) { int i; master_t *m; for (i = 0, m = masters.master; i < MAX_MASTERS; i++, m++) { if (m->state != ms_used) continue; // master slot unused if (NET_CompareAddress(addr, &m->addr)) return m; } return NULL; }
/* * 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 }
/** * Responds to a Steam server query. * * @param s query string * @param socket response socket * @param address response address * @param inmsg message for arguments * @return whether the request was handled as a Steam query */ bool SV_SteamServerQuery( const char *s, const socket_t *socket, const netadr_t *address, msg_t *inmsg ) { #if APP_STEAMID if( sv.state < ss_loading || sv.state > ss_game ) return false; // server not running if( ( !sv_public->integer && !NET_IsLANAddress( address ) ) || ( sv_maxclients->integer == 1 ) ) return false; if( !strcmp( s, "i" ) ) { // ping const char pingResponse[] = "j00000000000000"; Netchan_OutOfBand( socket, address, sizeof( pingResponse ), ( const uint8_t * )pingResponse ); return true; } if( !strcmp( s, "W" ) || !strcmp( s, "U\xFF\xFF\xFF\xFF" ) ) { // challenge - security feature, but since we don't send multiple packets always return 0 const uint8_t challengeResponse[] = { 'A', 0, 0, 0, 0 }; Netchan_OutOfBand( socket, address, sizeof( challengeResponse ), ( const uint8_t * )challengeResponse ); return true; } if( !strcmp( s, "TSource Engine Query" ) ) { // server info char hostname[MAX_INFO_VALUE]; char gamedir[MAX_QPATH]; char gamename[128]; char version[32]; char tags[MAX_STEAMQUERY_TAG_STRING]; int i, players = 0, bots = 0, maxclients = 0; int flags = 0x80 | 0x01; // game port | game ID containing app ID client_t *cl; msg_t msg; uint8_t msgbuf[MAX_STEAMQUERY_PACKETLEN - sizeof( int32_t )]; if( sv_showInfoQueries->integer ) Com_Printf( "Steam Info Packet %s\n", NET_AddressToString( address ) ); Q_strncpyz( hostname, COM_RemoveColorTokens( sv_hostname->string ), sizeof( hostname ) ); if( !hostname[0] ) Q_strncpyz( hostname, sv_hostname->dvalue, sizeof( hostname ) ); Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) ); Q_strncpyz( gamename, APPLICATION, sizeof( gamename ) ); if( Cvar_Value( "g_instagib" ) ) Q_strncatz( gamename, " IG", sizeof( gamename ) ); if( sv.configstrings[CS_GAMETYPETITLE][0] || sv.configstrings[CS_GAMETYPENAME][0] ) { Q_strncatz( gamename, " ", sizeof( gamename ) ); Q_strncatz( gamename, sv.configstrings[sv.configstrings[CS_GAMETYPETITLE][0] ? CS_GAMETYPETITLE : CS_GAMETYPENAME], sizeof( gamename ) ); } for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state >= CS_CONNECTED ) { if( cl->tvclient ) // exclude TV from the max players count continue; if( cl->edict->r.svflags & SVF_FAKECLIENT ) bots++; players++; } maxclients++; } Q_snprintfz( version, sizeof( version ), "%i.%i.0.0", APP_VERSION_MAJOR, APP_VERSION_MINOR ); SV_GetSteamTags( tags ); if( tags[0] ) flags |= 0x20; MSG_Init( &msg, msgbuf, sizeof( msgbuf ) ); MSG_WriteByte( &msg, 'I' ); MSG_WriteByte( &msg, APP_PROTOCOL_VERSION ); MSG_WriteString( &msg, hostname ); MSG_WriteString( &msg, sv.mapname ); MSG_WriteString( &msg, gamedir ); MSG_WriteString( &msg, gamename ); MSG_WriteShort( &msg, 0 ); // app ID specified later MSG_WriteByte( &msg, min( players, 99 ) ); MSG_WriteByte( &msg, min( maxclients, 99 ) ); MSG_WriteByte( &msg, min( bots, 99 ) ); MSG_WriteByte( &msg, ( dedicated && dedicated->integer ) ? 'd' : 'l' ); MSG_WriteByte( &msg, STEAMQUERY_OS ); MSG_WriteByte( &msg, Cvar_String( "password" )[0] ? 1 : 0 ); MSG_WriteByte( &msg, 0 ); // VAC insecure MSG_WriteString( &msg, version ); MSG_WriteByte( &msg, flags ); // port MSG_WriteShort( &msg, sv_port->integer ); // tags if( flags & 0x20 ) MSG_WriteString( &msg, tags ); // 64-bit game ID - needed to specify app ID MSG_WriteLong( &msg, APP_STEAMID & 0xffffff ); MSG_WriteLong( &msg, 0 ); Netchan_OutOfBand( socket, address, msg.cursize, msg.data ); return true; } if( s[0] == 'U' ) { // players msg_t msg; uint8_t msgbuf[MAX_STEAMQUERY_PACKETLEN - sizeof( int32_t )]; int i, players = 0; client_t *cl; char name[MAX_NAME_BYTES]; unsigned int time = Sys_Milliseconds(); if( sv_showInfoQueries->integer ) Com_Printf( "Steam Players Packet %s\n", NET_AddressToString( address ) ); MSG_Init( &msg, msgbuf, sizeof( msgbuf ) ); MSG_WriteByte( &msg, 'D' ); MSG_WriteByte( &msg, 0 ); for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( ( cl->state < CS_CONNECTED ) || cl->tvclient ) continue; Q_strncpyz( name, COM_RemoveColorTokens( cl->name ), sizeof( name ) ); if( ( msg.cursize + 10 + strlen( name ) ) > sizeof( msgbuf ) ) break; MSG_WriteByte( &msg, i ); MSG_WriteString( &msg, name ); MSG_WriteLong( &msg, cl->edict->r.client->r.frags ); MSG_WriteFloat( &msg, ( float )( time - cl->lastconnect ) * 0.001f ); players++; if( players == 99 ) break; } msgbuf[1] = players; Netchan_OutOfBand( socket, address, msg.cursize, msg.data ); return true; } if( !strcmp( s, "s" ) ) { // master server query, terminated by \n, followed by the challenge int i; bool fromMaster = false; int challenge; char gamedir[MAX_QPATH], basedir[MAX_QPATH], tags[MAX_STEAMQUERY_TAG_STRING]; int players = 0, bots = 0, maxclients = 0; client_t *cl; char msg[MAX_STEAMQUERY_PACKETLEN]; for( i = 0; i < MAX_MASTERS; i++ ) { if( sv_masters[i].steam && NET_CompareAddress( address, &sv_masters[i].address ) ) { fromMaster = true; break; } } if( !fromMaster ) return true; if( sv_showInfoQueries->integer ) Com_Printf( "Steam Master Server Info Packet %s\n", NET_AddressToString( address ) ); challenge = MSG_ReadLong( inmsg ); Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) ); Q_strncpyz( basedir, FS_BaseGameDirectory(), sizeof( basedir ) ); SV_GetSteamTags( tags ); for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state >= CS_CONNECTED ) { if( cl->tvclient ) // exclude TV from the max players count continue; if( cl->edict->r.svflags & SVF_FAKECLIENT ) bots++; players++; } maxclients++; } Q_snprintfz( msg, sizeof( msg ), "0\n\\protocol\\7\\challenge\\%i" // protocol must be 7 to match Source "\\players\\%i\\max\\%i\\bots\\%i" "\\gamedir\\%s\\map\\%s" "\\password\\%i\\os\\%c" "\\lan\\%i\\region\\255" "%s%s" "\\type\\%c\\secure\\0" "\\version\\%i.%i.0.0" "\\product\\%s\n", challenge, min( players, 99 ), min( maxclients, 99 ), min( bots, 99 ), gamedir, sv.mapname, Cvar_String( "password" )[0] ? 1 : 0, STEAMQUERY_OS, sv_public->integer ? 0 : 1, tags[0] ? "\\gametype\\" /* legacy - "gametype", not "tags" */ : "", tags, ( dedicated && dedicated->integer ) ? 'd' : 'l', APP_VERSION_MAJOR, APP_VERSION_MINOR, basedir ); NET_SendPacket( socket, ( const uint8_t * )msg, strlen( msg ), address ); return true; } if( s[0] == 'O' ) { // out of date message static bool printed = false; if( !printed ) { int i; for( i = 0; i < MAX_MASTERS; i++ ) { if( sv_masters[i].steam && NET_CompareAddress( address, &sv_masters[i].address ) ) { Com_Printf( "Server is out of date and cannot be added to the Steam master servers.\n" ); printed = true; return true; } } } return true; } #endif return false; }
/* * 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 }
static void FWD_network_update(void) { fd_set rfds; struct timeval tv; int retval; int i1; peer_t *p; FD_ZERO(&rfds); // select on main server socket FD_SET(net_socket, &rfds); i1 = net_socket + 1; for (p = peers; p; p = p->next) { // select on peers sockets FD_SET(p->s, &rfds); if (p->s >= i1) i1 = p->s + 1; } // if not DLL - read stdin #ifndef APP_DLL #ifndef _WIN32 // try read stdin only if connected to a terminal. if (isatty(STDIN) && isatty(STDOUT)) { FD_SET(STDIN, &rfds); if (STDIN >= i1) i1 = STDIN + 1; } #endif // _WIN32 #endif /* Sleep for some time, wake up immidiately if there input packet. */ tv.tv_sec = 0; tv.tv_usec = 100000; // 100 ms retval = select(i1, &rfds, (fd_set *)0, (fd_set *)0, &tv); // read console input. // NOTE: we do not do that if we are in DLL mode... Sys_ReadSTDIN(&ps, rfds); if (retval <= 0) return; // if we have input packet on main server/proxy socket, then read it if(FD_ISSET(net_socket, &rfds)) { qbool connectionless; int cnt; // read it for(;;) { if (!NET_GetPacket(net_socket, &net_message)) break; // check for bans. if (SV_IsBanned(&net_from)) continue; if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK) { QRY_SV_PingReply(); continue; } MSG_BeginReading(); connectionless = (MSG_ReadLong() == -1); if (connectionless) { if (MSG_BadRead()) continue; if (!SV_ConnectionlessPacket()) continue; // seems we do not need forward it } // search in peers for (p = peers; p; p = p->next) { // we have this peer already, so forward/send packet to remote server if (NET_CompareAddress(&p->from, &net_from)) break; } // peer was not found if (!p) continue; // forward data to the server/proxy if (p->ps >= ps_connected) { cnt = 1; // one packet by default // check for "drop" aka client disconnect, // first 10 bytes for NON connectionless packet is netchan related shit in QW if (p->proto == pr_qw && !connectionless && net_message.cursize > 10 && net_message.data[10] == clc_stringcmd) { if (!strcmp((char*)net_message.data + 10 + 1, "drop")) { // Sys_Printf("peer drop detected\n"); p->ps = ps_drop; // drop peer ASAP cnt = 3; // send few packets due to possibile packet lost } } for ( ; cnt > 0; cnt--) NET_SendPacket(p->s, net_message.cursize, net_message.data, &p->to); } time(&p->last); } } // now lets check peers sockets, perhaps we have input packets too for (p = peers; p; p = p->next) { if(FD_ISSET(p->s, &rfds)) { // yeah, we have packet, read it then for (;;) { if (!NET_GetPacket(p->s, &net_message)) break; // check for bans. if (SV_IsBanned(&net_from)) continue; // we should check is this packet from remote server, this may be some evil packet from haxors... if (!NET_CompareAddress(&p->to, &net_from)) continue; MSG_BeginReading(); if (MSG_ReadLong() == -1) { if (MSG_BadRead()) continue; if (!CL_ConnectionlessPacket(p)) continue; // seems we do not need forward it NET_SendPacket(net_socket, net_message.cursize, net_message.data, &p->from); continue; } if (p->ps >= ps_connected) NET_SendPacket(net_socket, net_message.cursize, net_message.data, &p->from); // qqshka: commented out // time(&p->last); } // for (;;) } // if(FD_ISSET(p->s, &rfds)) if (p->ps == ps_challenge) { // send challenge time to time if (time(NULL) - p->connect > 2) { p->connect = time(NULL); Netchan_OutOfBandPrint(p->s, &p->to, "getchallenge%s", p->proto == pr_qw ? "\n" : ""); } } } // for (p = peers; p; p = p->next) }