Exemple #1
0
/**
 * @sa SV_Spawn_f
 */
static void SV_Begin_f (client_t *cl)
{
	qboolean began;

	Com_DPrintf(DEBUG_SERVER, "Begin() from %s\n", cl->name);

	/* could be abused to respawn or cause spam/other mod-specific problems */
	if (cl->state != cs_spawning) {
		Com_Printf("EXPLOIT: Illegal 'begin' from %s (already spawned), client dropped.\n", cl->name);
		SV_DropClient(cl, "Illegal begin\n");
		return;
	}

	/* call the game begin function */
	TH_MutexLock(svs.serverMutex);
	began = svs.ge->ClientBegin(cl->player);
	TH_MutexUnlock(svs.serverMutex);

	if (!began) {
		SV_DropClient(cl, "'begin' failed\n");
		return;
	}
	SV_SetClientState(cl, cs_began);

	Cbuf_InsertFromDefer();
}
/**
 * @brief Called when the player is totally leaving the server, either willingly
 * or unwillingly. This is NOT called if the entire server is quitting
 * or crashing.
 */
void SV_DropClient (client_t * drop, const char *message)
{
	/* add the disconnect */
	struct dbuffer *msg = new_dbuffer();
	NET_WriteByte(msg, svc_disconnect);
	NET_WriteString(msg, message);
	NET_WriteMsg(drop->stream, msg);
	SV_BroadcastPrintf(PRINT_CHAT, "%s was dropped from the server - reason: %s\n", drop->name, message);

	if (drop->state == cs_spawned || drop->state == cs_spawning) {
		/* call the prog function for removing a client */
		/* this will remove the body, among other things */
		TH_MutexLock(svs.serverMutex);
		svs.ge->ClientDisconnect(drop->player);
		TH_MutexUnlock(svs.serverMutex);
	}

	NET_StreamFinished(drop->stream);
	drop->stream = NULL;

	drop->player->inuse = qfalse;
	SV_SetClientState(drop, cs_free);
	drop->name[0] = 0;

	if (svs.abandon) {
		int count = 0;
		client_t *cl = NULL;
		while ((cl = SV_GetNextClient(cl)) != NULL)
			if (cl->state >= cs_connected)
				count++;
		if (count == 0)
			svs.killserver = qtrue;
	}
}
Exemple #3
0
/**
 * @sa free_element
 */
static struct dbuffer_element * allocate_element (void)
{
	struct dbuffer_element *e;

	TH_MutexLock(dbuf_lock);

	if (free_elements == 0) {
		struct dbuffer_element *newBuf = (struct dbuffer_element *)Mem_PoolAlloc(sizeof(struct dbuffer_element), com_genericPool, 0);
		newBuf->next = free_element_list;
		free_element_list = newBuf;
		free_elements++;
		allocated_elements++;
	}
	assert(free_element_list);

	e = free_element_list;
	free_element_list = free_element_list->next;
	assert(free_elements > 0);
	free_elements--;
	e->space = DBUFFER_ELEMENT_SIZE;
	e->len = 0;
	e->next = NULL;

	TH_MutexUnlock(dbuf_lock);

	return e;
}
Exemple #4
0
/**
 * @brief Deallocate a dbuffer
 * @param[in,out] buf the dbuffer to deallocate
 * Deallocates a dbuffer, and all memory it uses
 */
void free_dbuffer (struct dbuffer *buf)
{
	struct dbuffer_element *e;
	if (!buf)
		return;
	while ((e = buf->head)) {
		buf->head = e->next;
		free_element(e);
	}

	TH_MutexLock(dbuf_lock);

	buf->next_free = free_dbuffer_list;
	free_dbuffer_list = buf;
	free_dbuffers++;

	/* now we should free them, as we are still having a lot of them allocated */
	while (free_dbuffers > DBUFFER_FREE_THRESHOLD) {
		buf = free_dbuffer_list;
		free_dbuffer_list = buf->next_free;
		Mem_Free(buf);
		free_dbuffers--;
		allocated_dbuffers--;
	}

	TH_MutexUnlock(dbuf_lock);
}
Exemple #5
0
/**
 * @brief Allocate a dbuffer
 * @return the newly allocated buffer
 * Allocates a new dbuffer and initialises it to be empty
 */
struct dbuffer * new_dbuffer (void)
{
	struct dbuffer *buf;

	TH_MutexLock(dbuf_lock);

	if (free_dbuffers == 0) {
		struct dbuffer *newBuf = (struct dbuffer *)Mem_PoolAlloc(sizeof(struct dbuffer), com_genericPool, 0);
		newBuf->next_free = free_dbuffer_list;
		free_dbuffer_list = newBuf;
		free_dbuffers++;
		allocated_dbuffers++;
	}
	assert(free_dbuffer_list);

	buf = free_dbuffer_list;
	free_dbuffer_list = free_dbuffer_list->next_free;
	assert(free_dbuffers > 0);
	free_dbuffers--;

	TH_MutexUnlock(dbuf_lock);

	buf->len = 0;
	buf->space = DBUFFER_ELEMENT_SIZE;
	buf->head = buf->tail = allocate_element();
	buf->start = buf->end = &buf->head->data[0];

	return buf;
}
Exemple #6
0
/**
 * @brief Lock the shared data by the calling thread.
 */
void ThreadLock (void)
{
	if (threadstate.numthreads == 1) {
		/* do nothing */
	} else if (lock != NULL && TH_MutexLock(lock) != -1) {
		/* already locked */
	} else {
		Sys_Error("Couldn't lock mutex (%p)!", (void*)lock);
	}
}
Exemple #7
0
/**
 * @brief Thread for the game frame function
 * @sa SV_RunGameFrame
 * @sa SV_Frame
 */
int SV_RunGameFrameThread (void *data)
{
	do {
		TH_MutexLock(svs.serverMutex);
		TH_MutexCondWait(svs.serverMutex, svs.gameFrameCond);
		SV_RunGameFrame();
		TH_MutexUnlock(svs.serverMutex);
	} while (!sv->endgame);

	return 0;
}
Exemple #8
0
/**
 * @sa SV_Begin_f
 */
static void SV_StartMatch_f (client_t *cl)
{
	Com_DPrintf(DEBUG_SERVER, "StartMatch() from %s\n", cl->name);

	if (cl->state != cs_spawned) {
		SV_DropClient(cl, "Invalid state\n");
		return;
	}

	TH_MutexLock(svs.serverMutex);
	svs.ge->ClientStartMatch(cl->player);
	TH_MutexUnlock(svs.serverMutex);

	Cbuf_InsertFromDefer();
}
Exemple #9
0
/**
 * @sa allocate_element
 */
static void free_element (struct dbuffer_element *e)
{
	TH_MutexLock(dbuf_lock);

	e->next = free_element_list;
	free_element_list = e;
	free_elements++;
	while (free_elements > DBUFFER_ELEMENTS_FREE_THRESHOLD) {
		e = free_element_list;
		free_element_list = e->next;
		Mem_Free(e);
		free_elements--;
		allocated_elements--;
	}

	TH_MutexUnlock(dbuf_lock);
}
Exemple #10
0
/**
 * @sa SV_ExecuteClientMessage
 */
static void SV_ExecuteUserCommand (client_t * cl, const char *s)
{
	const ucmd_t *u;

	Cmd_TokenizeString(s, qfalse);

	for (u = ucmds; u->name; u++)
		if (Q_streq(Cmd_Argv(0), u->name)) {
			Com_DPrintf(DEBUG_SERVER, "SV_ExecuteUserCommand: %s\n", s);
			u->func(cl);
			return;
		}

	if (Com_ServerState() == ss_game) {
		Com_DPrintf(DEBUG_SERVER, "SV_ExecuteUserCommand: client command: %s\n", s);
		TH_MutexLock(svs.serverMutex);
		svs.ge->ClientCommand(cl->player);
		TH_MutexUnlock(svs.serverMutex);
	}
}
Exemple #11
0
/**
 * @brief Pull specific info from a newly changed userinfo string into a more C friendly form.
 */
void SV_UserinfoChanged (client_t * cl)
{
	unsigned int i;

	/* call prog code to allow overrides */
	TH_MutexLock(svs.serverMutex);
	svs.ge->ClientUserinfoChanged(cl->player, cl->userinfo);
	TH_MutexUnlock(svs.serverMutex);

	/* name of the player */
	Q_strncpyz(cl->name, Info_ValueForKey(cl->userinfo, "cl_name"), sizeof(cl->name));
	/* mask off high bit */
	for (i = 0; i < sizeof(cl->name); i++)
		cl->name[i] &= 127;

	/* msg command */
	cl->messagelevel = Info_IntegerForKey(cl->userinfo, "cl_msg");

	Com_DPrintf(DEBUG_SERVER, "SV_UserinfoChanged: Changed userinfo for player %s\n", cl->name);
}
Exemple #12
0
/**
 * @brief Change the server to a new map, taking all connected clients along with it.
 * @note the full syntax is: @code map [day|night] [+]<map> [<assembly>] @endcode
 * @sa SV_AssembleMap
 * @sa CM_LoadMap
 * @sa Com_SetServerState
 */
void SV_Map (qboolean day, const char *levelstring, const char *assembly)
{
	int i;
	unsigned checksum = 0;
	char * map = SV_GetConfigString(CS_TILES);
	char * pos = SV_GetConfigString(CS_POSITIONS);
	mapInfo_t *randomMap = NULL;
	client_t *cl;

	/* any partially connected client will be restarted */
	Com_SetServerState(ss_restart);

	/* the game is just starting */
	SV_InitGame();

	if (!svs.initialized) {
		Com_Printf("Could not spawn the server\n");
		return;
	}

	assert(levelstring[0] != '\0');

	Com_DPrintf(DEBUG_SERVER, "SpawnServer: %s\n", levelstring);

	/* save name for levels that don't set message */
	SV_SetConfigString(CS_NAME, levelstring);
	SV_SetConfigString(CS_LIGHTMAP, day);

	Q_strncpyz(sv->name, levelstring, sizeof(sv->name));

	/* set serverinfo variable */
	sv_mapname = Cvar_FullSet("sv_mapname", sv->name, CVAR_SERVERINFO | CVAR_NOSET);

	/* notify the client in case of a listening server */
	SCR_BeginLoadingPlaque();

	if (assembly)
		Q_strncpyz(sv->assembly, assembly, sizeof(sv->assembly));
	else
		sv->assembly[0] = '\0';

	/* leave slots at start for clients only */
	cl = NULL;
	while ((cl = SV_GetNextClient(cl)) != NULL) {
		/* needs to reconnect */
		if (cl->state >= cs_spawning)
			SV_SetClientState(cl, cs_connected);
	}

	/* assemble and load the map */
	if (levelstring[0] == '+') {
		randomMap = SV_AssembleMap(levelstring + 1, assembly, map, pos, 0);
		if (!randomMap) {
			Com_Printf("Could not load assembly for map '%s'\n", levelstring);
			return;
		}
	} else {
		SV_SetConfigString(CS_TILES, levelstring);
		SV_SetConfigString(CS_POSITIONS, assembly ? assembly : "");
	}

	CM_LoadMap(map, day, pos, &sv->mapData, &sv->mapTiles);

	Com_Printf("checksum for the map '%s': %u\n", levelstring, sv->mapData.mapChecksum);
	SV_SetConfigString(CS_MAPCHECKSUM, sv->mapData.mapChecksum);

	checksum = Com_GetScriptChecksum();

	Com_Printf("ufo script checksum %u\n", checksum);
	SV_SetConfigString(CS_UFOCHECKSUM, checksum);
	SV_SetConfigString(CS_OBJECTAMOUNT, csi.numODs);
	SV_SetConfigString(CS_VERSION, UFO_VERSION);
	SV_SetConfigString(CS_MAPTITLE, SV_GetMapTitle(randomMap, levelstring));
	if (Q_strstart(SV_GetConfigString(CS_MAPTITLE), "b/")) {
		/* For base attack, CS_MAPTITLE contains too many chars */
		SV_SetConfigString(CS_MAPTITLE, "Base attack");
		SV_SetConfigString(CS_NAME, ".baseattack");
	}

	/* clear random-map assembly data */
	Mem_Free(randomMap);
	randomMap = NULL;

	/* clear physics interaction links */
	SV_ClearWorld();

	/* fix this! */
	for (i = 1; i <= sv->mapData.numInline; i++)
		sv->models[i] = CM_InlineModel(&sv->mapTiles, va("*%i", i));

	/* precache and static commands can be issued during map initialization */
	Com_SetServerState(ss_loading);

	TH_MutexLock(svs.serverMutex);
	/* load and spawn all other entities */
	svs.ge->SpawnEntities(sv->name, SV_GetConfigStringInteger(CS_LIGHTMAP), sv->mapData.mapEntityString);
	TH_MutexUnlock(svs.serverMutex);

	/* all precaches are complete */
	Com_SetServerState(ss_game);

	Com_Printf("-------------------------------------\n");

	Cbuf_CopyToDefer();
}
Exemple #13
0
/**
 * @brief The current net_message is parsed for the given client
 */
void SV_ExecuteClientMessage (client_t * cl, int cmd, struct dbuffer *msg)
{
	if (cmd == -1)
		return;

	switch (cmd) {
	default:
		Com_Printf("SV_ExecuteClientMessage: unknown command char '%d'\n", cmd);
		SV_DropClient(cl, "Unknown command\n");
		return;

	case clc_nop:
		break;

	case clc_ack:
		cl->lastmessage = svs.realtime;
		break;

	case clc_userinfo:
		NET_ReadString(msg, cl->userinfo, sizeof(cl->userinfo));
		Com_DPrintf(DEBUG_SERVER, "userinfo from client: %s\n", cl->userinfo);
		SV_UserinfoChanged(cl);
		break;

	case clc_stringcmd:
	{
		char str[1024];
		NET_ReadString(msg, str, sizeof(str));

		Com_DPrintf(DEBUG_SERVER, "stringcmd from client: %s\n", str);
		SV_ExecuteUserCommand(cl, str);

		if (cl->state == cs_free)
			return;			/* disconnect command */
		break;
	}

	case clc_action:
		/* client actions are handled by the game module */
		sv->messageBuffer = msg;
		TH_MutexLock(svs.serverMutex);
		svs.ge->ClientAction(cl->player);
		TH_MutexUnlock(svs.serverMutex);
		sv->messageBuffer = NULL;
		break;

	case clc_endround:
		/* player wants to end round */
		sv->messageBuffer = msg;
		TH_MutexLock(svs.serverMutex);
		svs.ge->ClientEndRound(cl->player);
		TH_MutexUnlock(svs.serverMutex);
		sv->messageBuffer = NULL;
		break;

	case clc_teaminfo:
		/* player sends team info */
		/* actors spawn accordingly */
		sv->messageBuffer = msg;
		TH_MutexLock(svs.serverMutex);
		svs.ge->ClientTeamInfo(cl->player);
		TH_MutexUnlock(svs.serverMutex);
		sv->messageBuffer = NULL;
		SV_SetClientState(cl, cs_spawned);
		break;

	case clc_initactorstates:
		/* player sends team info */
		/* actors spawn accordingly */
		sv->messageBuffer = msg;
		TH_MutexLock(svs.serverMutex);
		svs.ge->ClientInitActorStates(cl->player);
		TH_MutexUnlock(svs.serverMutex);
		sv->messageBuffer = NULL;
		break;
	}
}
Exemple #14
0
/**
 * @brief A connection request that did not come from the master
 * @sa CL_ConnectionlessPacket
 */
static void SVC_DirectConnect (struct net_stream *stream)
{
	char userinfo[MAX_INFO_STRING];
	client_t *cl;
	player_t *player;
	int playernum;
	int version;
	qboolean connected;
	char buf[256];
	const char *peername = NET_StreamPeerToName(stream, buf, sizeof(buf), qfalse);

	Com_DPrintf(DEBUG_SERVER, "SVC_DirectConnect()\n");

	if (sv->started || sv->spawned) {
		Com_Printf("rejected connect because match is already running\n");
		NET_OOB_Printf(stream, "print\nGame has started already.\n");
		return;
	}

	version = atoi(Cmd_Argv(1));
	if (version != PROTOCOL_VERSION) {
		Com_Printf("rejected connect from version %i - %s\n", version, peername);
		NET_OOB_Printf(stream, "print\nServer is version %s.\n", UFO_VERSION);
		return;
	}

	Q_strncpyz(userinfo, Cmd_Argv(2), sizeof(userinfo));

	if (userinfo[0] == '\0') {  /* catch empty userinfo */
		Com_Printf("Empty userinfo from %s\n", peername);
		NET_OOB_Printf(stream, "print\nConnection refused.\n");
		return;
	}

	if (strchr(userinfo, '\xFF')) {  /* catch end of message in string exploit */
		Com_Printf("Illegal userinfo contained xFF from %s\n", peername);
		NET_OOB_Printf(stream, "print\nConnection refused.\n");
		return;
	}

	if (strlen(Info_ValueForKey(userinfo, "ip"))) {  /* catch spoofed ips  */
		Com_Printf("Illegal userinfo contained ip from %s\n", peername);
		NET_OOB_Printf(stream, "print\nConnection refused.\n");
		return;
	}

	/* force the IP key/value pair so the game can filter based on ip */
	Info_SetValueForKey(userinfo, sizeof(userinfo), "ip", peername);

	/* find a client slot */
	cl = NULL;
	while ((cl = SV_GetNextClient(cl)) != NULL)
		if (cl->state == cs_free)
			break;
	if (cl == NULL) {
		NET_OOB_Printf(stream, "print\nServer is full.\n");
		Com_Printf("Rejected a connection - server is full.\n");
		return;
	}

	/* build a new connection - accept the new client
	 * this is the only place a client_t is ever initialized */
	OBJZERO(*cl);
	playernum = cl - SV_GetClient(0);
	player = PLAYER_NUM(playernum);
	cl->player = player;
	cl->player->num = playernum;

	TH_MutexLock(svs.serverMutex);
	connected = svs.ge->ClientConnect(player, userinfo, sizeof(userinfo));
	TH_MutexUnlock(svs.serverMutex);

	/* get the game a chance to reject this connection or modify the userinfo */
	if (!connected) {
		const char *rejmsg = Info_ValueForKey(userinfo, "rejmsg");
		if (rejmsg[0] != '\0') {
			NET_OOB_Printf(stream, "print\n%s\nConnection refused.\n", rejmsg);
			Com_Printf("Game rejected a connection from %s. Reason: %s\n", peername, rejmsg);
		} else {
			NET_OOB_Printf(stream, "print\nConnection refused.\n");
			Com_Printf("Game rejected a connection from %s.\n", peername);
		}
		return;
	}

	/* new player */
	cl->player->inuse = qtrue;
	cl->lastmessage = svs.realtime;

	/* parse some info from the info strings */
	strncpy(cl->userinfo, userinfo, sizeof(cl->userinfo) - 1);
	SV_UserinfoChanged(cl);

	/* send the connect packet to the client */
	if (sv_http_downloadserver->string[0])
		NET_OOB_Printf(stream, "client_connect dlserver=%s", sv_http_downloadserver->string);
	else
		NET_OOB_Printf(stream, "client_connect");

	SV_SetClientState(cl, cs_connected);

	Q_strncpyz(cl->peername, peername, sizeof(cl->peername));
	cl->stream = stream;
	NET_StreamSetData(stream, cl);
}