Esempio n. 1
0
/*
 * Sv_New_f
 *
 * Sends the first message from the server to a connected client.
 * This will be sent on the initial connection and upon each server load.
 */
static void Sv_New_f(void) {
	int player_num;

	Com_Debug("New() from %s\n", Sv_NetaddrToString(sv_client));

	if (sv_client->state != SV_CLIENT_CONNECTED) {
		Com_Warn("Sv_New_f: %s already spawned\n",
				Sv_NetaddrToString(sv_client));
		return;
	}

	// demo servers will send the demo file's server info packet
	if (sv.state == SV_ACTIVE_DEMO) {
		return;
	}

	// send the server data
	Msg_WriteByte(&sv_client->netchan.message, SV_CMD_SERVER_DATA);
	Msg_WriteLong(&sv_client->netchan.message, PROTOCOL);
	Msg_WriteLong(&sv_client->netchan.message, svs.spawn_count);
	Msg_WriteLong(&sv_client->netchan.message, svs.frame_rate);
	Msg_WriteByte(&sv_client->netchan.message, 0);
	Msg_WriteString(&sv_client->netchan.message, Cvar_GetString("game"));

	player_num = sv_client - svs.clients;
	Msg_WriteShort(&sv_client->netchan.message, player_num);

	// send full level name
	Msg_WriteString(&sv_client->netchan.message, sv.config_strings[CS_NAME]);

	// begin fetching config_strings
	Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT);
	Msg_WriteString(&sv_client->netchan.message,
			va("config_strings %i 0\n", svs.spawn_count));
}
Esempio n. 2
0
/*
 * Sv_Begin_f
 */
static void Sv_Begin_f(void) {

	Com_Debug("Begin() from %s\n", Sv_NetaddrToString(sv_client));

	if (sv_client->state != SV_CLIENT_CONNECTED) { // catch duplicate spawns
		Com_Warn("Sv_Begin_f: Invalid Begin() from %s\n",
				Sv_NetaddrToString(sv_client));
		Sv_KickClient(sv_client, NULL);
		return;
	}

	if (sv.state == SV_ACTIVE_DEMO)
		return;

	// handle the case of a level changing while a client was connecting
	if (strtoul(Cmd_Argv(1), NULL, 0) != svs.spawn_count) {
		Com_Debug("Sv_Begin_f: Stale spawn count from %s\n",
				Sv_NetaddrToString(sv_client));
		Sv_New_f();
		return;
	}

	sv_client->state = SV_CLIENT_ACTIVE;

	// call the game begin function
	svs.game->ClientBegin(sv_player);

	Cbuf_InsertFromDefer();
}
Esempio n. 3
0
/*
 * Sv_UserInfoChanged
 *
 * Enforces safe user_info data before passing onto game module.
 */
void Sv_UserInfoChanged(sv_client_t *cl) {
	char *val;
	size_t i;

	if (*cl->user_info == '\0') { // catch empty user_info
		Com_Print("Empty user_info from %s\n", Sv_NetaddrToString(cl));
		Sv_KickClient(cl, "Bad user info");
		return;
	}

	if (strchr(cl->user_info, '\xFF')) { // catch end of message exploit
		Com_Print("Illegal user_info contained xFF from %s\n",
				Sv_NetaddrToString(cl));
		Sv_KickClient(cl, "Bad user info");
		return;
	}

	if (!ValidateUserInfo(cl->user_info)) { // catch otherwise invalid user_info
		Com_Print("Invalid user_info from %s\n", Sv_NetaddrToString(cl));
		Sv_KickClient(cl, "Bad user info");
		return;
	}

	val = GetUserInfo(cl->user_info, "skin");
	if (strstr(val, "..")) // catch malformed skins
		SetUserInfo(cl->user_info, "skin", "enforcer/qforcer");

	// call game code to allow overrides
	svs.game->ClientUserInfoChanged(cl->edict, cl->user_info);

	// name for C code, mask off high bit
	strncpy(cl->name, GetUserInfo(cl->user_info, "name"), sizeof(cl->name) - 1);
	for (i = 0; i < sizeof(cl->name); i++) {
		cl->name[i] &= 127;
	}

	// rate command
	val = GetUserInfo(cl->user_info, "rate");
	if (*val != '\0') {
		cl->rate = atoi(val);

		if (cl->rate > CLIENT_RATE_MAX)
			cl->rate = CLIENT_RATE_MAX;
		else if (cl->rate < CLIENT_RATE_MIN)
			cl->rate = CLIENT_RATE_MIN;
	}

	// limit the print messages the client receives
	val = GetUserInfo(cl->user_info, "message_level");
	if (*val != '\0') {
		cl->message_level = atoi(val);
	}

	// start/stop sending view angles for demo recording
	val = GetUserInfo(cl->user_info, "recording");
	cl->recording = atoi(val) == 1;
}
Esempio n. 4
0
/*
 * Sv_ConfigStrings_f
 */
static void Sv_ConfigStrings_f(void) {
	unsigned int start;

	Com_Debug("ConfigStrings() from %s\n", Sv_NetaddrToString(sv_client));

	if (sv_client->state != SV_CLIENT_CONNECTED) {
		Com_Warn("Sv_ConfigStrings_f: %s already spawned\n",
				Sv_NetaddrToString(sv_client));
		return;
	}

	// handle the case of a level changing while a client was connecting
	if (strtoul(Cmd_Argv(1), NULL, 0) != svs.spawn_count) {
		Com_Debug("Sv_ConfigStrings_f: Stale spawn count from %s\n",
				Sv_NetaddrToString(sv_client));
		Sv_New_f();
		return;
	}

	start = strtoul(Cmd_Argv(2), NULL, 0);

	if (start >= MAX_CONFIG_STRINGS) { // catch bad offsets
		Com_Warn("Sv_ConfigStrings_f: Bad config_string offset from %s\n",
				Sv_NetaddrToString(sv_client));
		Sv_KickClient(sv_client, NULL);
		return;
	}

	// write a packet full of data
	while (sv_client->netchan.message.size < MAX_MSG_SIZE / 2 && start
			< MAX_CONFIG_STRINGS) {
		if (sv.config_strings[start][0]) {
			Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CONFIG_STRING);
			Msg_WriteShort(&sv_client->netchan.message, start);
			Msg_WriteString(&sv_client->netchan.message,
					sv.config_strings[start]);
		}
		start++;
	}

	// send next command
	if (start == MAX_CONFIG_STRINGS) {
		Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT);
		Msg_WriteString(&sv_client->netchan.message,
				va("baselines %i 0\n", svs.spawn_count));
	} else {
		Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT);
		Msg_WriteString(&sv_client->netchan.message,
				va("config_strings %i %i\n", svs.spawn_count, start));
	}
}
Esempio n. 5
0
/*
 * Sv_CheckCommandTimes
 *
 * Once per second, gives all clients an allotment of 1000 milliseconds
 * for their movement commands which will be decremented as we receive
 * new information from them.  If they drift by a significant margin
 * over the next interval, assume they are trying to cheat.
 */
static void Sv_CheckCommandTimes(void) {
	static unsigned int last_check_time = -9999;
	int i;

	if (svs.real_time < last_check_time) { // wrap around from last level
		last_check_time = -9999;
	}

	// see if its time to check the movements
	if (svs.real_time - last_check_time < CMD_MSEC_CHECK_INTERVAL) {
		return;
	}

	last_check_time = svs.real_time;

	// inspect each client, ensuring they are reasonably in sync with us
	for (i = 0; i < sv_max_clients->integer; i++) {
		sv_client_t *cl = &svs.clients[i];

		if (cl->state < SV_CLIENT_ACTIVE) {
			continue;
		}

		if (sv_enforce_time->value) { // check them

			if (abs(cl->cmd_msec) > CMD_MSEC_ALLOWABLE_DRIFT) { // irregular movement
				cl->cmd_msec_errors++;

				Com_Debug("%s drifted %dms\n", Sv_NetaddrToString(cl),
						cl->cmd_msec);

				if (cl->cmd_msec_errors >= sv_enforce_time->value) {
					Com_Warn("Sv_CheckCommandTimes: Too many errors from %s\n",
							Sv_NetaddrToString(cl));
					Sv_KickClient(cl, "Irregular movement");
					continue;
				}
			} else { // normal movement

				if (cl->cmd_msec_errors) {
					cl->cmd_msec_errors--;
				}
			}
		}

		cl->cmd_msec = CMD_MSEC_CHECK_INTERVAL; // reset for next cycle
	}
}
Esempio n. 6
0
/*
 * Sv_Baselines_f
 */
static void Sv_Baselines_f(void) {
	unsigned int start;
	entity_state_t nullstate;
	entity_state_t *base;

	Com_Debug("Baselines() from %s\n", Sv_NetaddrToString(sv_client));

	if (sv_client->state != SV_CLIENT_CONNECTED) {
		Com_Warn("Sv_Baselines_f: %s already spawned\n",
				Sv_NetaddrToString(sv_client));
		return;
	}

	// handle the case of a level changing while a client was connecting
	if (strtoul(Cmd_Argv(1), NULL, 0) != svs.spawn_count) {
		Com_Debug("Sv_Baselines_f: Stale spawn count from %s\n",
				Sv_NetaddrToString(sv_client));
		Sv_New_f();
		return;
	}

	start = strtoul(Cmd_Argv(2), NULL, 0);

	memset(&nullstate, 0, sizeof(nullstate));

	// write a packet full of data
	while (sv_client->netchan.message.size < MAX_MSG_SIZE / 2 && start
			< MAX_EDICTS) {
		base = &sv.baselines[start];
		if (base->model1 || base->sound || base->effects) {
			Msg_WriteByte(&sv_client->netchan.message, SV_CMD_ENTITY_BASELINE);
			Msg_WriteDeltaEntity(&nullstate, base, &sv_client->netchan.message,
					true, true);
		}
		start++;
	}

	// send next command
	if (start == MAX_EDICTS) {
		Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT);
		Msg_WriteString(&sv_client->netchan.message,
				va("precache %i\n", svs.spawn_count));
	} else {
		Msg_WriteByte(&sv_client->netchan.message, SV_CMD_CBUF_TEXT);
		Msg_WriteString(&sv_client->netchan.message,
				va("baselines %i %i\n", svs.spawn_count, start));
	}
}
Esempio n. 7
0
/*
 * Sv_UserStringCommand
 *
 * Invoke the specified user string command.  If we don't have a function for
 * it, pass it off to the game module.
 */
static void Sv_UserStringCommand(const char *s) {
	sv_user_string_cmd_t *c;

	Cmd_TokenizeString(s);

	if (strchr(s, '\xFF')) { // catch end of message exploit
		Com_Warn("Sv_ExecuteUserCommand: Illegal command from %s\n",
				Sv_NetaddrToString(sv_client));
		Sv_KickClient(sv_client, NULL);
		return;
	}

	for (c = sv_user_string_cmds; c->name; c++) {

		if (!strcmp(Cmd_Argv(0), c->name)) {
			c->func();
			break;
		}
	}

	if (!c->name) { // unmatched command

		if (sv.state == SV_ACTIVE_GAME) // maybe the game knows what to do with it
			svs.game->ClientCommand(sv_player);
	}
}
Esempio n. 8
0
/*
 * @brief Enforces safe user_info data before passing onto game module.
 */
void Sv_UserInfoChanged(sv_client_t *cl) {
	char *val;
	size_t i;

	if (*cl->user_info == '\0') { // catch empty user_info
		Com_Print("Empty user_info from %s\n", Sv_NetaddrToString(cl));
		Sv_KickClient(cl, "Bad user info");
		return;
	}

	if (strchr(cl->user_info, '\xFF')) { // catch end of message exploit
		Com_Print("Illegal user_info contained xFF from %s\n", Sv_NetaddrToString(cl));
		Sv_KickClient(cl, "Bad user info");
		return;
	}

	if (!ValidateUserInfo(cl->user_info)) { // catch otherwise invalid user_info
		Com_Print("Invalid user_info from %s\n", Sv_NetaddrToString(cl));
		Sv_KickClient(cl, "Bad user info");
		return;
	}

	// call game code to allow overrides
	svs.game->ClientUserInfoChanged(cl->entity, cl->user_info);

	// name for C code, mask off high bit
	g_strlcpy(cl->name, GetUserInfo(cl->user_info, "name"), sizeof(cl->name));
	for (i = 0; i < sizeof(cl->name); i++) {
		cl->name[i] &= 127;
	}

	// rate command
	val = GetUserInfo(cl->user_info, "rate");
	if (*val != '\0') {
		cl->rate = strtoul(val, NULL, 10);
		if (cl->rate > 0 && cl->rate < CLIENT_RATE_MIN) {
			cl->rate = CLIENT_RATE_MIN;
		}
	}

	// limit the print messages the client receives
	val = GetUserInfo(cl->user_info, "message_level");
	if (*val != '\0') {
		cl->message_level = strtoul(val, NULL, 10);
	}
}
Esempio n. 9
0
/*
 * Sv_ParseClientMessage
 *
 * The current net_message is parsed for the given client.
 */
void Sv_ParseClientMessage(sv_client_t *cl) {
	user_cmd_t null_cmd, oldest_cmd, old_cmd, new_cmd;
	int net_drop;
	int strings_issued;
	int moves_issued;
	int last_frame;
	int c;
	char *s;

	sv_client = cl;
	sv_player = sv_client->edict;

	// allow a finite number of moves and strings
	moves_issued = strings_issued = 0;

	while (true) {

		if (net_message.read > net_message.size) {
			Com_Warn("Sv_ParseClientMessage: Bad read from %s\n",
					Sv_NetaddrToString(sv_client));
			Sv_DropClient(cl);
			return;
		}

		c = Msg_ReadByte(&net_message);
		if (c == -1)
			break;

		switch (c) {

		case CL_CMD_USER_INFO:
			strncpy(cl->user_info, Msg_ReadString(&net_message), sizeof(cl->user_info) - 1);
			Sv_UserInfoChanged(cl);
			break;

		case CL_CMD_MOVE:
			if (++moves_issued > CMD_MAX_MOVES) {
				return; // someone is trying to cheat
			}

			last_frame = Msg_ReadLong(&net_message);
			if (last_frame != cl->last_frame) {
				cl->last_frame = last_frame;
				if (cl->last_frame > -1) {
					cl->frame_latency[cl->last_frame & (CLIENT_LATENCY_COUNTS
							- 1)] = svs.real_time - cl->frames[cl->last_frame
							& UPDATE_MASK].sent_time;
				}
			}

			memset(&null_cmd, 0, sizeof(null_cmd));
			Msg_ReadDeltaUsercmd(&net_message, &null_cmd, &oldest_cmd);
			Msg_ReadDeltaUsercmd(&net_message, &oldest_cmd, &old_cmd);
			Msg_ReadDeltaUsercmd(&net_message, &old_cmd, &new_cmd);

			// don't start delta compression until the client is spawned
			// TODO: should this be a little higher up?
			if (cl->state != SV_CLIENT_ACTIVE) {
				cl->last_frame = -1;
				break;
			}

			// catch extremely high msec movements
			if (null_cmd.msec > CMD_MAX_MSEC || oldest_cmd.msec > CMD_MAX_MSEC
					|| old_cmd.msec > CMD_MAX_MSEC || new_cmd.msec
					> CMD_MAX_MSEC) {
				Com_Warn("Sv_ParseClientMessage: Illegal msec from %s\n",
						Sv_NetaddrToString(cl));
				Sv_KickClient(cl, "Illegal movement");
				return;
			}

			net_drop = cl->netchan.dropped;
			if (net_drop < 20) {
				while (net_drop > 2) {
					Sv_ClientThink(cl, &cl->last_cmd);
					net_drop--;
				}
				if (net_drop > 1)
					Sv_ClientThink(cl, &oldest_cmd);
				if (net_drop > 0)
					Sv_ClientThink(cl, &old_cmd);
			}
			Sv_ClientThink(cl, &new_cmd);
			cl->last_cmd = new_cmd;
			break;

		case CL_CMD_STRING:
			s = Msg_ReadString(&net_message);

			// malicious users may try using too many string commands
			if (++strings_issued < CMD_MAX_STRINGS)
				Sv_UserStringCommand(s);
			else {
				Com_Warn(
						"Sv_ParseClientMessage: CMD_MAX_STRINGS exceeded for %s\n",
						Sv_NetaddrToString(cl));
				Sv_KickClient(cl, "Too many commands.");
				return;
			}

			if (cl->state == SV_CLIENT_FREE)
				return; // disconnect command
			break;

		default:
			Com_Print("Sv_ParseClientMessage: unknown command %d\n", c);
			Sv_DropClient(cl);
			return;
		}
	}
}
Esempio n. 10
0
/*
 * Sv_Download_f
 */
static void Sv_Download_f(void) {
	const char *name;
	void *buf;
	unsigned int i = 0, offset = 0;

	name = Cmd_Argv(1);

	if (Cmd_Argc() > 2)
		offset = strtoul(Cmd_Argv(2), NULL, 0); // downloaded offset

	// catch illegal offset or file_names
	if (*name == '.' || *name == '/' || *name == '\\' || strstr(
			name, "..")) {
		Com_Warn("Sv_Download_f: Malicious download (%s:%d) from %s\n", name,
				offset, Sv_NetaddrToString(sv_client));
		Sv_KickClient(sv_client, NULL);
		return;
	}

	while (downloadable[i]) { // ensure download name is allowed
		if (GlobMatch(downloadable[i], name))
			break;
		i++;
	}

	if (!downloadable[i]) { // it wasn't
		Com_Warn("Sv_Download_f: Illegal download (%s) from %s\n", name,
				Sv_NetaddrToString(sv_client));
		Sv_KickClient(sv_client, NULL);
		return;
	}

	if (!sv_udp_download->value) { // lastly, ensure server wishes to allow
		Msg_WriteByte(&sv_client->netchan.message, SV_CMD_DOWNLOAD);
		Msg_WriteShort(&sv_client->netchan.message, -1);
		Msg_WriteByte(&sv_client->netchan.message, 0);
		return;
	}

	if (sv_client->download) // free last download
		Fs_FreeFile(sv_client->download);

	sv_client->download_size = Fs_LoadFile(name, &buf);
	sv_client->download = (byte *) buf;
	sv_client->download_count = offset;

	if (offset > sv_client->download_size)
		sv_client->download_count = sv_client->download_size;

	if (!sv_client->download) { // legal file name, but missing file
		Com_Warn("Sv_Download_f: Couldn't download %s to %s\n", name,
				Sv_NetaddrToString(sv_client));
		Msg_WriteByte(&sv_client->netchan.message, SV_CMD_DOWNLOAD);
		Msg_WriteShort(&sv_client->netchan.message, -1);
		Msg_WriteByte(&sv_client->netchan.message, 0);
		return;
	}

	Sv_NextDownload_f();
	Com_Debug("Downloading %s to %s\n", name, sv_client->name);
}