static void CL_RemoveQFInfoKeys (void) { Info_RemoveKey (cls.userinfo, "*cap"); Info_RemoveKey (cls.userinfo, "*qf_version"); Info_RemoveKey (cls.userinfo, "*qsg_version"); }
static void ResetMiscCommands(void) { Cbuf_AddText("mapgroup clear\n"); Cbuf_AddText("skygroup clear\n"); MarkDefaultSources(); Info_RemoveKey(cls.userinfo, "ec"); Info_RemoveKey(cls.userinfo, "exec_class"); Info_RemoveKey(cls.userinfo, "em"); Info_RemoveKey(cls.userinfo, "exec_map"); }
/* ================== Info_SetValueForKey Changes or adds a key/value pair ================== */ void Info_SetValueForKey( char *s, const char *key, const char *value ) { char newi[MAX_INFO_STRING]; const char* blacklist = "\\;\""; if ( strlen( s ) >= MAX_INFO_STRING ) { Com_Printf( 0, "Info_SetValueForKey: oversize infostring"); return; } for(; *blacklist; ++blacklist) { if (strchr (key, *blacklist) || strchr (value, *blacklist)) { Com_Printf (0, "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value); return; } } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; _snprintf (newi, sizeof(newi), "\\%s\\%s", key, value); if (strlen(newi) + strlen(s) >= MAX_INFO_STRING) { Com_Printf (0, "Info string length exceeded\n"); return; } strcat (newi, s); strcpy (s, newi); }
/* ================== SV_ForceCvar_f_helper Called internally by SV_ForceCvar_f. ================== */ static void SV_ForceCvar_f_helper( client_t *cl ) { int oldInfoLen; int newInfoLen; qboolean touchedUserinfo = qfalse; // Who knows what would happen if we called the VM with a GAME_CLIENT_USERINFO_CHANGED // when this client wasn't connected. if (cl->state < CS_CONNECTED) { return; } // First remove all keys; there might exist more than one in the userinfo. oldInfoLen = strlen(cl->userinfo); while (qtrue) { Info_RemoveKey(cl->userinfo, Cmd_Argv(2)); newInfoLen = strlen(cl->userinfo); if (oldInfoLen == newInfoLen) { break; } // userinfo wasn't modified. oldInfoLen = newInfoLen; touchedUserinfo = qtrue; } if (strlen(Cmd_Argv(3)) > 0) { if (strlen(Cmd_Argv(2)) + strlen(Cmd_Argv(3)) + 2 + newInfoLen >= MAX_INFO_STRING) { SV_DropClient(cl, "userinfo string length exceeded"); return; } Info_SetValueForKey(cl->userinfo, Cmd_Argv(2), Cmd_Argv(3)); touchedUserinfo = qtrue; } if (touchedUserinfo) { SV_UserinfoChanged(cl); VM_Call(gvm, GAME_CLIENT_USERINFO_CHANGED, cl - svs.clients); } }
void SV_SetLocalinfo (const char *key, const char *value) { char *oldvalue = 0; if (sv_funcs.LocalinfoChanged) oldvalue = strdup (Info_ValueForKey (localinfo, key)); if (*value) Info_SetValueForKey (localinfo, key, value, !sv_highchars->int_val); else Info_RemoveKey (localinfo, key); if (sv_funcs.LocalinfoChanged) { *sv_globals.time = sv.time; *sv_globals.self = 0; PR_PushFrame (&sv_pr_state); PR_RESET_PARAMS (&sv_pr_state); P_STRING (&sv_pr_state, 0) = PR_SetTempString (&sv_pr_state, key); P_STRING (&sv_pr_state, 1) = PR_SetTempString (&sv_pr_state, oldvalue); P_STRING (&sv_pr_state, 2) = PR_SetTempString (&sv_pr_state, value); PR_ExecuteProgram (&sv_pr_state, sv_funcs.LocalinfoChanged); PR_PopFrame (&sv_pr_state); } if (oldvalue) free (oldvalue); }
void Info_RemovePrefixedKeys (char *start, char prefix) { char *s, pkey[512], value[512], *o; s = start; while (1) { if (*s == '\\') s++; o = pkey; while (*s != '\\') { if (!*s) return; *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) return; *o++ = *s++; } *o = 0; if (pkey[0] == prefix) { Info_RemoveKey (start, pkey); s = start; } if (!*s) return; } }
/* ================== Info_SetValueForKey Changes or adds a key/value pair ================== */ void Info_SetValueForKey( char *s, const char *key, const char *value ) { char newi[MAX_INFO_STRING]; Info_RemoveKey (s, key); if (!value || !strlen(value)) return; _snprintf (newi, sizeof(newi), "\\%s\\%s", key, value); strcat (newi, s); strcpy (s, newi); }
/* ================= Info_SetValueForKey ================= */ void Info_SetValueForKey (char *string, char *key, char *value){ char newString[MAX_INFO_STRING], *s; int c; if (strchr(key, '\\') || strchr(value, '\\')){ Com_Printf("Can't use keys or values with a \\\n"); return; } if (strchr(key, ';') || strchr(value, ';')){ Com_Printf("Can't use keys or values with a ;\n"); return; } if (strchr(key, '\"') || strchr(value, '\"')){ Com_Printf("Can't use keys or values with a \"\n"); return; } if (strlen(key) >= MAX_INFO_KEY){ Com_Printf("Keys must be < %i characters\n", MAX_INFO_KEY); return; } if (strlen(value) >= MAX_INFO_VALUE){ Com_Printf("Values must be < %i characters\n", MAX_INFO_VALUE); return; } Info_RemoveKey(string, key); if (!value || !value[0]) return; Q_snprintfz(newString, sizeof(newString), "\\%s\\%s", key, value); if (strlen(newString) + strlen(string) > MAX_INFO_STRING){ Com_Printf("Info string length exceeded\n"); return; } // Only copy ASCII values string += strlen(string); s = newString; while (*s){ c = *s++; c &= 127; // Strip high bits if (c >= 32 && c < 127) *string++ = c; } *string = 0; }
void Info_RemovePrefixedKeys (char *start, char prefix) { char *s; char pkey[512]; char value[512]; char *o; s = start; while (1) { int nCount; if (*s == '\\') s++; nCount = 0; o = pkey; while ( (*s != '\\') && (nCount < sizeof(pkey)-1) ) { if (!*s) return; *o++ = *s++; nCount++; } *o = 0; s++; nCount = 0; o = value; while ( (*s != '\\') && *s && (nCount < sizeof(value)-1) ) { if (!*s) return; *o++ = *s++; nCount++; } *o = 0; if (pkey[0] == prefix) { Info_RemoveKey (start, pkey); s = start; } if (!*s) return; } }
/* =================== SV_FullClientUpdate Writes all update values to a sizebuf =================== */ void SV_FullClientUpdate (client_t *client, sizebuf_t *buf) { int i; char info[MAX_INFO_STRING]; i = client - svs.clients; if (client->state == cs_free && sv_fastconnect.value) return; MSG_WriteByte (buf, svc_updatefrags); MSG_WriteByte (buf, i); MSG_WriteShort (buf, client->old_frags); MSG_WriteByte (buf, svc_updateping); MSG_WriteByte (buf, i); MSG_WriteShort (buf, SV_CalcPing (client)); MSG_WriteByte (buf, svc_updatepl); MSG_WriteByte (buf, i); MSG_WriteByte (buf, client->lossage); MSG_WriteByte (buf, svc_updateentertime); MSG_WriteByte (buf, i); MSG_WriteFloat (buf, svs.realtime - client->connection_started); strcpy (info, client->userinfo); Info_RemovePrefixedKeys (info, '_'); // server passwords, etc Info_RemoveKey (info, "pmodel"); Info_RemoveKey (info, "emodel"); MSG_WriteByte (buf, svc_updateuserinfo); MSG_WriteByte (buf, i); MSG_WriteLong (buf, client->userid); MSG_WriteString (buf, info); }
void Info_SetValueForKey(VStr& s, const VStr& key, const VStr& value) { guard(Info_SetValueForKey); if (s.Length() >= MAX_INFO_STRING) { Host_Error("Info_SetValueForKey: oversize infostring"); } if (strchr(*key, '\\') || strchr(*value, '\\')) { GCon->Log("Can't use keys or values with a \\"); return; } if (strchr(*key, '\"') || strchr(*value, '\"')) { GCon->Log("Can't use keys or values with a \""); return; } // this next line is kinda trippy VStr v = Info_ValueForKey(s, key); if (v.IsNotEmpty()) { // Key exists, make sure we have enough room for new value, if we // don't, don't change it! if (value.Length() - v.Length() + s.Length() > MAX_INFO_STRING) { GCon->Log("Info string length exceeded"); return; } } Info_RemoveKey(s, key); if (value.IsEmpty()) return; VStr newi = VStr("\\") + key + "\\" + value; if (newi.Length() + s.Length() > MAX_INFO_STRING) { GCon->Log("Info string length exceeded"); return; } s = s + newi; unguard; }
/* ================== Info_SetValueForKey ================== */ qboolean Info_SetValueForKey( char *s, const char *key, const char *value ) { char newi[MAX_INFO_STRING], *v; size_t l, kl, vl; int c; // validate key kl = Info_SubValidate( key ); if( kl >= MAX_QPATH ) { return qfalse; } // validate value vl = Info_SubValidate( value ); if( vl >= MAX_QPATH ) { return qfalse; } Info_RemoveKey( s, key ); if( !vl ) { return qtrue; } l = strlen( s ); if( l + kl + vl + 2 >= MAX_INFO_STRING ) { return qfalse; } newi[0] = '\\'; memcpy( newi + 1, key, kl ); newi[kl + 1] = '\\'; memcpy( newi + kl + 2, value, vl + 1 ); // only copy ascii values s += l; v = newi; while( *v ) { c = *v++; c &= 127; // strip high bits if( Q_isprint( c ) ) *s++ = c; } *s = 0; return qtrue; }
/* ================== Info_SetValueForKey Changes or adds a key/value pair ================== */ void Info_SetValueForKey( char *s, const char *key, const char *value ) { char newi[MAX_INFO_STRING]; if ( strlen( s ) >= MAX_INFO_STRING ) { Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" ); return; } if (strchr (key, '\\') || strchr (value, '\\')) { Com_Printf ("Can't use keys or values with a \\\n"); return; } if (strchr (key, ';') || strchr (value, ';')) { Com_Printf ("Can't use keys or values with a semicolon\n"); return; } if (strchr (key, '\"') || strchr (value, '\"')) { Com_Printf ("Can't use keys or values with a \"\n"); return; } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; snprintf (newi, sizeof(newi), "\\%s\\%s", key, value); if (strlen(newi) + strlen(s) > MAX_INFO_STRING) { Com_Printf ("Info string length exceeded\n"); return; } strcat (newi, s); strcpy (s, newi); }
/** * @brief Adds a new entry into string with given value. * @note Removed any old version of the key * @param[in,out] s The target info string * @param[in] size The size of @c s * @param[in] key The key to set * @param[in] value The value to set for the given key * @sa Info_RemoveKey * @sa Info_SetValueForKeyAsInteger */ void Info_SetValueForKey (char* s, const size_t size, const char* key, const char* value) { char newi[MAX_INFO_STRING]; if (strstr(key, "\\") || strstr(value, "\\")) { Com_Printf("Can't use keys or values with a \\\n"); return; } if (strstr(key, ";")) { Com_Printf("Can't use keys or values with a semicolon\n"); return; } if (strstr(key, "\"") || strstr(value, "\"")) { Com_Printf("Can't use keys or values with a \"\n"); return; } if (strlen(key) > MAX_INFO_KEY - 1) { Com_Printf("Keys must be < " DOUBLEQUOTE(MAX_INFO_KEY) " characters.\n"); return; } if (strlen(key) > MAX_INFO_VALUE - 1) { Com_Printf("Values must be < " DOUBLEQUOTE(MAX_INFO_VALUE) " characters.\n"); return; } Info_RemoveKey(s, key); if (Q_strnull(value)) return; Com_sprintf(newi, sizeof(newi), "\\%s\\%s%s", key, value, s); Q_strncpyz(s, newi, size); }
/* ================== SV_DirectConnect A "connect" OOB command has been received ================== */ void SV_DirectConnect( netadr_t from, const Cmd::Args& args ) { char userinfo[ MAX_INFO_STRING ]; int i; client_t *cl, *newcl; client_t temp; sharedEntity_t *ent; int clientNum; int version; int qport; int challenge; const char *password; int startIndex; bool denied; char reason[ MAX_STRING_CHARS ]; int count; const char *ip; #ifdef HAVE_GEOIP const char *country = nullptr; #endif if ( args.Argc() < 2 ) { return; } Log::Debug( "SVC_DirectConnect ()" ); Q_strncpyz( userinfo, args.Argv(1).c_str(), sizeof( userinfo ) ); // DHM - Nerve :: Update Server allows any protocol to connect // NOTE TTimo: but we might need to store the protocol around for potential non http/ftp clients version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); if ( version != PROTOCOL_VERSION ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\nServer uses protocol version %i (yours is %i).", PROTOCOL_VERSION, version ); Log::Debug( " rejected connect from version %i", version ); return; } challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); qport = atoi( Info_ValueForKey( userinfo, "qport" ) ); // quick reject for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { // DHM - Nerve :: This check was allowing clients to reconnect after zombietime(2 secs) //if ( cl->state == CS_FREE ) { //continue; //} if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { if ( ( svs.time - cl->lastConnectTime ) < ( sv_reconnectlimit->integer * 1000 ) ) { Log::Debug( "%s: reconnect rejected: too soon", NET_AdrToString( from ) ); return; } break; } } if ( NET_IsLocalAddress( from ) ) { ip = "localhost"; } else { ip = NET_AdrToString( from ); } if ( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\nUserinfo string length exceeded. " "Try removing setu cvars from your config." ); return; } Info_SetValueForKey( userinfo, "ip", ip, false ); // see if the challenge is valid (local clients don't need to challenge) if ( !NET_IsLocalAddress( from ) ) { int ping; for ( i = 0; i < MAX_CHALLENGES; i++ ) { if ( NET_CompareAdr( from, svs.challenges[ i ].adr ) ) { if ( challenge == svs.challenges[ i ].challenge ) { break; // good } } } if ( i == MAX_CHALLENGES ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]No or bad challenge for address." ); return; } // force the IP address key/value pair, so the game can filter based on it Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ), false ); if ( svs.challenges[ i ].firstPing == 0 ) { ping = svs.time - svs.challenges[ i ].pingTime; svs.challenges[ i ].firstPing = ping; } else { ping = svs.challenges[ i ].firstPing; } #ifdef HAVE_GEOIP country = NET_GeoIP_Country( &from ); if ( country ) { Log::Notice( "Client %i connecting from %s with %i challenge ping\n", i, country, ping ); } else { Log::Notice( "Client %i connecting from somewhere unknown with %i challenge ping\n", i, ping ); } #else Log::Notice( "Client %i connecting with %i challenge ping\n", i, ping ); #endif svs.challenges[ i ].connected = true; // never reject a LAN client based on ping if ( !Sys_IsLANAddress( from ) ) { if ( sv_minPing->value && ping < sv_minPing->value ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]Server is for high pings only" ); Log::Debug( "Client %i rejected on a too low ping", i ); return; } if ( sv_maxPing->value && ping > sv_maxPing->value ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]Server is for low pings only" ); Log::Debug( "Client %i rejected on a too high ping: %i", i, ping ); return; } } } else { // force the "ip" info key to "localhost" Info_SetValueForKey( userinfo, "ip", "localhost", false ); } newcl = &temp; memset( newcl, 0, sizeof( client_t ) ); // if there is already a slot for this IP address, reuse it for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if ( cl->state == clientState_t::CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { Log::Notice( "%s:reconnect\n", NET_AdrToString( from ) ); newcl = cl; // this doesn't work because it nukes the players userinfo // // disconnect the client from the game first so any flags the // // player might have are dropped // VM_Call( gvm, GAME_CLIENT_DISCONNECT, newcl - svs.clients ); // goto gotnewcl; } } // find a client slot // if "sv_privateClients" is set > 0, then that number // of client slots will be reserved for connections that // have "password" set to the value of "sv_privatePassword" // Info requests will report the maxclients as if the private // slots didn't exist, to prevent people from trying to connect // to a full server. // This is to allow us to reserve a couple slots here on our // servers so we can play without having to kick people. // check for privateClient password password = Info_ValueForKey( userinfo, "password" ); if ( !strcmp( password, sv_privatePassword->string ) ) { startIndex = 0; } else { // skip past the reserved slots startIndex = sv_privateClients->integer; } newcl = nullptr; for ( i = startIndex; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[ i ]; if ( cl->state == clientState_t::CS_FREE ) { newcl = cl; break; } } if ( !newcl ) { if ( NET_IsLocalAddress( from ) ) { count = 0; for ( i = startIndex; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[ i ]; if ( SV_IsBot(cl) ) { count++; } } // if they're all bots if ( count >= sv_maxclients->integer - startIndex ) { SV_DropClient( &svs.clients[ sv_maxclients->integer - 1 ], "only bots on server" ); newcl = &svs.clients[ sv_maxclients->integer - 1 ]; } else { Com_Error( errorParm_t::ERR_FATAL, "server is full on local connect" ); } } else { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n%s", sv_fullmsg->string ); Log::Debug( "Rejected a connection." ); return; } } // we got a newcl, so reset the reliableSequence and reliableAcknowledge cl->reliableAcknowledge = 0; cl->reliableSequence = 0; gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = std::move(temp); clientNum = newcl - svs.clients; ent = SV_GentityNum( clientNum ); newcl->gentity = ent; ent->r.svFlags = 0; #ifdef HAVE_GEOIP if ( country ) { Info_SetValueForKey( userinfo, "geoip", country, false ); } #endif // save the challenge newcl->challenge = challenge; // save the address Netchan_Setup( netsrc_t::NS_SERVER, &newcl->netchan, from, qport ); // init the netchan queue // Save the pubkey Q_strncpyz( newcl->pubkey, Info_ValueForKey( userinfo, "pubkey" ), sizeof( newcl->pubkey ) ); Info_RemoveKey( userinfo, "pubkey", false ); // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof( newcl->userinfo ) ); // get the game a chance to reject this connection or modify the userinfo denied = gvm.GameClientConnect( reason, sizeof( reason ), clientNum, true, false ); // firstTime = true if ( denied ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]%s", reason ); Log::Debug( "Game rejected a connection: %s.", reason ); return; } SV_UserinfoChanged( newcl ); // DHM - Nerve :: Clear out firstPing now that client is connected svs.challenges[ i ].firstPing = 0; // send the connect packet to the client NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "connectResponse" ); Log::Debug( "Going from CS_FREE to CS_CONNECTED for %s", newcl->name ); newcl->state = clientState_t::CS_CONNECTED; newcl->nextSnapshotTime = svs.time; newcl->lastPacketTime = svs.time; newcl->lastConnectTime = svs.time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newcl->gamestateMessageNum = -1; // if this was the first client on the server, or the last client // the server can hold, send a heartbeat to the master. count = 0; for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if ( svs.clients[ i ].state >= clientState_t::CS_CONNECTED ) { count++; } } if ( count == 1 || count == sv_maxclients->integer ) { SV_Heartbeat_f(); } }
/* ================== SVC_DirectConnect A connection request that did not come from the master ================== */ void SVC_DirectConnect (void) { char userinfo[1024]; netadr_t adr; int i; client_t *cl, *newcl; edict_t *ent; int edictnum; char *s; int clients, spectators; qbool spectator; int qport; int version; int challenge; version = atoi(Cmd_Argv(1)); if (version != PROTOCOL_VERSION) { Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, QW_VERSION); Com_Printf ("* rejected connect from version %i\n", version); return; } qport = atoi(Cmd_Argv(2)); challenge = atoi(Cmd_Argv(3)); // note an extra byte is needed to replace spectator key strlcpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1); // see if the challenge is valid if (net_from.type != NA_LOOPBACK) { for (i=0 ; i<MAX_CHALLENGES ; i++) { if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) { if (challenge == svs.challenges[i].challenge) break; // good Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nBad challenge.\n", A2C_PRINT); return; } } if (i == MAX_CHALLENGES) { Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nNo challenge for address.\n", A2C_PRINT); return; } } // check for password or spectator_password s = Info_ValueForKey (userinfo, "spectator"); if (s[0] && strcmp(s, "0")) { if (sv_spectatorPassword.string[0] && Q_stricmp(sv_spectatorPassword.string, "none") && strcmp(sv_spectatorPassword.string, s) ) { // failed Com_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from)); Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT); return; } Info_RemoveKey (userinfo, "spectator"); Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING); spectator = true; } else { s = Info_ValueForKey (userinfo, "password"); if (sv_password.string[0] && Q_stricmp(sv_password.string, "none") && strcmp(sv_password.string, s) ) { Com_Printf ("%s:password failed\n", NET_AdrToString (net_from)); Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nserver requires a password\n\n", A2C_PRINT); return; } spectator = false; Info_RemoveKey (userinfo, "password"); } #ifdef MAUTH // Check that the client is allowed to connect... if( net_from.type != NA_LOOPBACK && !COM_CheckParm("-nomauth") ) { authclient_t *authclient; // Try the auth token queue first... authclient = SV_AuthListFind(&authtokq, Info_ValueForKey(userinfo, "name")); if( authclient == NULL ) { // Fall back to checking if they had already connected and are in the // client queue already (i.e. were on here before a map change)... authclient = SV_AuthListFind(&authclientq, Info_ValueForKey(userinfo, "name")); if( authclient == NULL ) { // FIXME drop with reason Com_Printf ("MAUTH: Client %s not in a queue; connect refused.\n", Info_ValueForKey(userinfo, "name")); return; } } else { // They're valid, so move them to the main client queue from the // auth cache queue... SV_AuthListMove(&authtokq, &authclientq, authclient); } // Move to auth'd clients queue if they're valid... if( !authclient->valid ) { // FIXME drop with reason Com_Printf ("MAUTH: Client %s not validated yet; connect refused.\n", Info_ValueForKey(userinfo, "name")); return; } //SV_AuthListPrint(&authtokq); //SV_AuthListPrint(&authclientq); Com_Printf ("MAUTH: Client %s connection allowed.\n", Info_ValueForKey(userinfo, "name")); } else { Com_Printf("MAUTH: loopback or disabled; allowing client connection.\n"); } #endif adr = net_from; // if there is already a slot for this ip, reuse it for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) && ( cl->netchan.qport == qport || adr.port == cl->netchan.remote_address.port )) { if (cl->state == cs_connected) { Com_Printf ("%s:dup connect\n", NET_AdrToString (adr)); return; } Com_Printf ("%s:reconnect\n", NET_AdrToString (adr)); if (cl->state == cs_spawned) { SV_DropClient (cl); SV_ClearReliable (cl); // don't send the disconnect } cl->state = cs_free; break; } } // count up the clients and spectators and find an empty client slot clients = spectators = 0; newcl = NULL; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++,cl++) { if (cl->state == cs_free) { if (!newcl) newcl = cl; // grab first available slot continue; } if (cl->spectator) spectators++; else clients++; } // if at server limits, refuse connection if ( (spectator && spectators >= (int)maxspectators.value) || (!spectator && clients >= (int)maxclients.value) || !newcl) { Com_Printf ("%s:full connect\n", NET_AdrToString (adr)); Netchan_OutOfBandPrint (NS_SERVER, adr, "%c\nserver is full\n\n", A2C_PRINT); return; } // build a new connection // accept the new client // this is the only place a client_t is ever initialized memset (newcl, 0, sizeof(*newcl)); newcl->userid = SV_GenerateUserID(); strlcpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)); Netchan_OutOfBandPrint (NS_SERVER, adr, "%c", S2C_CONNECTION ); Netchan_Setup (NS_SERVER, &newcl->netchan, adr, qport); newcl->state = cs_connected; SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf)); newcl->datagram.allowoverflow = true; // spectator mode can ONLY be set at join time newcl->spectator = spectator; // extract extensions bits newcl->extensions = atoi(Info_ValueForKey(newcl->userinfo, "*z_ext")); Info_RemoveKey (newcl->userinfo, "*z_ext"); #ifdef VWEP_TEST newcl->extensions |= atoi(Info_ValueForKey(newcl->userinfo, "*vwtest")) ? Z_EXT_VWEP : 0; Info_RemoveKey (newcl->userinfo, "*vwtest"); #endif // See if the client is using a proxy. The best test I can come up with for now... newcl->uses_proxy = *Info_ValueForKey(newcl->userinfo, "Qizmo") ? true : false; edictnum = (newcl - svs.clients) + 1; ent = EDICT_NUM(edictnum); ent->inuse = true; newcl->edict = ent; // parse some info from the info strings SV_ExtractFromUserinfo (newcl); // JACK: Init the floodprot stuff. for (i=0; i<10; i++) newcl->whensaid[i] = 0.0; newcl->whensaidhead = 0; newcl->lockedtill = 0; // call the progs to get default spawn parms for the new client PR_ExecuteProgram (pr_global_struct->SetNewParms); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i]; if (newcl->spectator) Com_Printf ("Spectator %s connected\n", newcl->name); else Com_DPrintf ("Client %s connected\n", newcl->name); newcl->sendinfo = true; }
void Info_SetValueForStarKey ( char *s, const char *key, const char *value, int maxsize ) { char news[1024], *v; int c; if (strstr (key, "\\") || strstr (value, "\\") ) { return; } if (strstr (key, "..") || strstr (value, "..") ) { // Con_Printf ("Can't use keys or values with a ..\n"); return; } if (strstr (key, "\"") || strstr (value, "\"") ) { return; } if (strlen(key) > MAX_KV_LEN || strlen(value) > MAX_KV_LEN) { return; } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; sprintf (news, "\\%s\\%s", key, value); if ( (int)(strlen(news) + strlen(s)) >= maxsize) { // no more room in buffer to add key/value if ( Info_IsKeyImportant( key ) ) { // keep removing the largest key/values until we have room char *largekey; do { largekey = Info_FindLargestKey( s, maxsize ); Info_RemoveKey( s, largekey ); } while ( ((int)(strlen(news) + strlen(s)) >= maxsize) && *largekey != 0 ); if ( largekey[0] == 0 ) { // no room to add setting return; } } else { // no room to add setting return; } } // only copy ascii values s += strlen(s); v = news; while (*v) { c = (unsigned char)*v++; // Strip out high ascii characters c &= 127; if (c > 13) { *s++ = c; } } *s = 0; }
/* <41063> ../engine/info.c:275 */ void Info_SetValueForStarKey(char *s, const char *key, const char *value, int maxsize) { char newArray[MAX_INFO_STRING]; char *v; int c; if (!key || !value) { Con_Printf("Keys and values can't be null\n"); return; } if (key[0] == 0) { Con_Printf("Keys can't be an empty string\n"); return; } if (Q_strstr(key, "\\") || Q_strstr(value, "\\")) { Con_Printf("Can't use keys or values with a \\\n"); return; } if (Q_strstr(key, "..") || Q_strstr(value, "..")) { // TODO: Why silently return? //Con_Printf("Can't use keys or values with a ..\n"); return; } if (Q_strstr(key, "\"") || Q_strstr(value, "\"")) { Con_Printf("Can't use keys or values with a \"\n"); return; } int keyLen = Q_strlen(key); int valueLan = Q_strlen(value); if (keyLen >= MAX_KV_LEN || valueLan >= MAX_KV_LEN) { Con_Printf("Keys and values must be < %i characters\n", MAX_KV_LEN); return; } if (!Q_UnicodeValidate(key) || !Q_UnicodeValidate(value)) { Con_Printf("Keys and values must be valid utf8 text\n"); return; } // Remove current key/value and return if we doesn't specified to set a value Info_RemoveKey(s, key); if (value[0] == 0) { return; } // Create key/value pair Q_snprintf(newArray, MAX_INFO_STRING - 1, "\\%s\\%s", key, value); newArray[MAX_INFO_STRING - 1] = 0; int neededLength = Q_strlen(newArray); if ((int)Q_strlen(s) + neededLength >= maxsize) { // no more room in the buffer to add key/value if (!Info_IsKeyImportant(key)) { // no room to add setting Con_Printf("Info string length exceeded\n"); return; } // keep removing the largest key/values until we have a room char *largekey; do { largekey = Info_FindLargestKey(s, maxsize); if (largekey[0] == 0) { // no room to add setting Con_Printf("Info string length exceeded\n"); return; } Info_RemoveKey(s, largekey); } while ((int)Q_strlen(s) + neededLength >= maxsize); } // auto lowercase team bool lowerCaseValue = Q_stricmp(key, "team") == 0; s += Q_strlen(s); v = newArray; while (*v) { c = (unsigned char)*v++; if (lowerCaseValue) { c = tolower(c); } *s++ = c; } *s = 0; }
/* * SV_UserinfoChanged * * Pull specific info from a newly changed userinfo string * into a more C friendly form. */ void SV_UserinfoChanged( client_t *client ) { char *val; int ival; assert( client ); assert( Info_Validate( client->userinfo ) ); if( !client->edict || !( client->edict->r.svflags & SVF_FAKECLIENT ) ) { // 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 ) ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (socket)\n" ); return; } if( !Info_SetValueForKey( client->userinfo, "ip", NET_AddressToString( &client->netchan.remoteAddress ) ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (ip)\n" ); return; } } // mm session ival = 0; val = Info_ValueForKey( client->userinfo, "cl_mm_session" ); if( val ) ival = atoi( val ); if( !val || ival != client->mm_session ) Info_SetValueForKey( client->userinfo, "cl_mm_session", va("%d", client->mm_session ) ); // mm login if( client->mm_login[0] != '\0' ) { Info_SetValueForKey( client->userinfo, "cl_mm_login", client->mm_login ); } else { Info_RemoveKey( client->userinfo, "cl_mm_login" ); } // call prog code to allow overrides ge->ClientUserinfoChanged( client->edict, client->userinfo ); if( !Info_Validate( client->userinfo ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Invalid userinfo (after game)" ); return; } // we assume that game module deals with setting a correct name val = Info_ValueForKey( client->userinfo, "name" ); if( !val || !val[0] ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: No name set" ); return; } Q_strncpyz( client->name, val, sizeof( client->name ) ); #ifndef RATEKILLED // rate command if( NET_IsLANAddress( &client->netchan.remoteAddress ) ) { client->rate = 99999; // lans should not rate limit } else { val = Info_ValueForKey( client->userinfo, "rate" ); if( val && val[0] ) { int newrate; newrate = atoi( val ); if( sv_maxrate->integer && newrate > sv_maxrate->integer ) newrate = sv_maxrate->integer; else if( newrate > 90000 ) newrate = 90000; if( newrate < 1000 ) newrate = 1000; if( client->rate != newrate ) { client->rate = newrate; Com_Printf( "%s%s has rate %i\n", client->name, S_COLOR_WHITE, client->rate ); } } else client->rate = 5000; } #endif }
/* ================== SVC_DirectConnect A connection request that did not come from the master ================== */ static void SVC_DirectConnect (void) { char userinfo[1024]; static int userid; netadr_t adr; int i; client_t *cl, *newcl; client_t temp; edict_t *ent; int edictnum; const char *s; int clients, spectators; qboolean spectator; q_strlcpy (userinfo, Cmd_Argv(2), sizeof(userinfo)); // check for password or spectator_password s = Info_ValueForKey (userinfo, "spectator"); if (s[0] && strcmp(s, "0")) { if (spectator_password.string[0] && q_strcasecmp(spectator_password.string, "none") && strcmp(spectator_password.string, s) ) { // failed Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from)); Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT); return; } Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING); spectator = true; Info_RemoveKey (userinfo, "spectator"); // remove passwd } else { s = Info_ValueForKey (userinfo, "password"); if (password.string[0] && q_strcasecmp(password.string, "none") && strcmp(password.string, s) ) { Con_Printf ("%s:password failed\n", NET_AdrToString (net_from)); Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT); return; } spectator = false; Info_RemoveKey (userinfo, "password"); // remove passwd } adr = net_from; userid++; // so every client gets a unique id newcl = &temp; memset (newcl, 0, sizeof(client_t)); newcl->userid = userid; newcl->portals = atoi(Cmd_Argv(1)); // works properly if (!sv_highchars.integer) { byte *p, *q; for (p = (byte *)newcl->userinfo, q = (byte *)userinfo; *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++) { if (*q > 31 && *q <= 127) *p++ = *q; } } else { q_strlcpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)); } // if there is already a slot for this ip, drop it for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (NET_CompareAdr (adr, cl->netchan.remote_address)) { Con_Printf ("%s:reconnect\n", NET_AdrToString (adr)); SV_DropClient (cl); break; } } // count up the clients and spectators clients = 0; spectators = 0; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (cl->spectator) spectators++; else clients++; } // if at server limits, refuse connection if (maxclients.integer > MAX_CLIENTS) Cvar_SetValue ("maxclients", MAX_CLIENTS); if (maxspectators.integer > MAX_CLIENTS) Cvar_SetValue ("maxspectators", MAX_CLIENTS); if (maxspectators.integer + maxclients.integer > MAX_CLIENTS) Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.integer + maxclients.integer); if ( (spectator && spectators >= maxspectators.integer) || (!spectator && clients >= maxclients.integer) ) { Con_Printf ("%s:full connect\n", NET_AdrToString (adr)); Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT); return; } // find a client slot newcl = NULL; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) { newcl = cl; break; } } if (!newcl) { Con_Printf ("WARNING: miscounted available clients\n"); return; } // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION ); edictnum = (newcl-svs.clients)+1; Netchan_Setup (&newcl->netchan, adr); newcl->state = cs_connected; SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf)); newcl->datagram.allowoverflow = true; // spectator mode can ONLY be set at join time newcl->spectator = spectator; ent = EDICT_NUM(edictnum); newcl->edict = ent; ED_ClearEdict (ent); // parse some info from the info strings SV_ExtractFromUserinfo (newcl); // JACK: Init the floodprot stuff. for (i = 0; i < 10; i++) newcl->whensaid[i] = 0.0; newcl->whensaidhead = 0; newcl->lockedtill = 0; // call the progs to get default spawn parms for the new client PR_ExecuteProgram (pr_global_struct->SetNewParms); for (i = 0; i < NUM_SPAWN_PARMS; i++) newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i]; if (newcl->spectator) Con_Printf ("Spectator %s connected\n", newcl->name); else Con_DPrintf ("Client %s connected\n", newcl->name); }
void Info_SetValueForStarKey ( char *s, const char *key, const char *value, int maxsize ) { char newch[1024], *v; int c; #ifdef SERVERONLY extern cvar_t sv_highchars; #endif if (strstr (key, "\\") || strstr (value, "\\") ) { return; } if (strstr (key, "\"") || strstr (value, "\"") ) { return; } if (strlen(key) > MAX_KV_LEN || strlen(value) > MAX_KV_LEN) { return; } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; sprintf (newch, "\\%s\\%s", key, value); if ( (int)(strlen(newch) + strlen(s)) >= maxsize) { return; } // only copy ascii values s += strlen(s); v = newch; while (*v) { c = (unsigned char)*v++; #ifndef SERVERONLY // client only allows highbits on name if (stricmp(key, "name") != 0) { c &= 127; if (c < 32 || c > 127) continue; // auto lowercase team if (stricmp(key, "team") == 0) c = tolower(c); } #else if (!sv_highchars.value) { c &= 127; if (c < 32 || c > 127) continue; } #endif // c &= 127; // strip high bits if (c > 13) // && c < 127) *s++ = (char)c; } *s = 0; }
/* * SV_LongInfoString * Builds the string that is sent as heartbeats and status replies */ static char *SV_LongInfoString( qboolean fullStatus ) { char tempstr[1024] = { 0 }; const char *gametype; static char status[MAX_MSGLEN - 16]; int i, bots, count; client_t *cl; size_t statusLength; size_t tempstrLength; Q_strncpyz( status, Cvar_Serverinfo(), sizeof( status ) ); // convert "g_gametype" to "gametype" gametype = Info_ValueForKey( status, "g_gametype" ); if( gametype ) { Info_RemoveKey( status, "g_gametype" ); Info_SetValueForKey( status, "gametype", gametype ); } statusLength = strlen( status ); bots = 0; count = 0; for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state >= CS_CONNECTED ) { if( cl->edict->r.svflags & SVF_FAKECLIENT || cl->tvclient ) bots++; count++; } } if( bots ) Q_snprintfz( tempstr, sizeof( tempstr ), "\\bots\\%i", bots ); Q_snprintfz( tempstr + strlen( tempstr ), sizeof( tempstr ) - strlen( tempstr ), "\\clients\\%i%s", count, fullStatus ? "\n" : "" ); tempstrLength = strlen( tempstr ); if( statusLength + tempstrLength >= sizeof( status ) ) return status; // can't hold any more Q_strncpyz( status + statusLength, tempstr, sizeof( status ) - statusLength ); statusLength += tempstrLength; if ( fullStatus ) { for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state >= CS_CONNECTED ) { Q_snprintfz( tempstr, sizeof( tempstr ), "%i %i \"%s\" %i\n", cl->edict->r.client->r.frags, cl->ping, cl->name, cl->edict->s.team ); tempstrLength = strlen( tempstr ); if( statusLength + tempstrLength >= sizeof( status ) ) break; // can't hold any more Q_strncpyz( status + statusLength, tempstr, sizeof( status ) - statusLength ); statusLength += tempstrLength; } } } return status; }
void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize) { char newv[1024], *v; int c; if (strstr (key, "\\") || strstr (value, "\\") ) { // printf ("Key has a slash\n"); return; } if (strstr (key, "\"") || strstr (value, "\"") ) { // printf ("Key has a quote\n"); return; } if (strlen(key) >= MAX_INFO_KEY || strlen(value) >= MAX_INFO_KEY) { // printf ("Key or value is too long\n"); return; } // this next line is kinda trippy if (*(v = Info_ValueForKey(s, key, newv, sizeof(newv)))) { // key exists, make sure we have enough room for new value, if we don't, // don't change it! if (strlen(value) - strlen(v) + strlen(s) + 1 > maxsize) { // Con_TPrintf (TL_INFOSTRINGTOOLONG); return; } } Info_RemoveKey (s, key); if (!value || !strlen(value)) return; snprintf (newv, sizeof(newv), "\\%s\\%s", key, value); if ((int)(strlen(newv) + strlen(s) + 1) > maxsize) { // printf ("info buffer is too small\n"); return; } // only copy ascii values s += strlen(s); v = newv; while (*v) { c = (unsigned char)*v++; // c &= 127; // strip high bits if (c > 13) // && c < 127) *s++ = c; } *s = 0; }