Ejemplo n.º 1
0
/*
 * @brief A connection request that did not come from the master.
 */
static void Svc_Connect(void) {
	char user_info[MAX_USER_INFO_STRING];
	sv_client_t *cl, *client;
	int32_t i;

	Com_Debug("Svc_Connect()\n");

	net_addr_t *addr = &net_from;

	const int32_t version = strtol(Cmd_Argv(1), NULL, 0);

	// resolve protocol
	if (version != PROTOCOL) {
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nServer is version %d.\n", PROTOCOL);
		return;
	}

	const uint8_t qport = strtoul(Cmd_Argv(2), NULL, 0);

	const uint32_t challenge = strtoul(Cmd_Argv(3), NULL, 0);

	// copy user_info, leave room for ip stuffing
	g_strlcpy(user_info, Cmd_Argv(4), sizeof(user_info) - 25);

	if (*user_info == '\0') { // catch empty user_info
		Com_Print("Empty user_info from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	if (strchr(user_info, '\xFF')) { // catch end of message in string exploit
		Com_Print("Illegal user_info contained xFF from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	if (strlen(GetUserInfo(user_info, "ip"))) { // catch spoofed ips
		Com_Print("Illegal user_info contained ip from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	if (!ValidateUserInfo(user_info)) { // catch otherwise invalid user_info
		Com_Print("Invalid user_info from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	// force the ip so the game can filter on it
	SetUserInfo(user_info, "ip", Net_NetaddrToString(addr));

	// enforce a valid challenge to avoid denial of service attack
	for (i = 0; i < MAX_CHALLENGES; i++) {
		if (Net_CompareClientNetaddr(addr, &svs.challenges[i].addr)) {
			if (challenge == svs.challenges[i].challenge) {
				svs.challenges[i].challenge = 0;
				break; // good
			}
			Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nBad challenge\n");
			return;
		}
	}
	if (i == MAX_CHALLENGES) {
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nNo challenge for address\n");
		return;
	}

	// resolve the client slot
	client = NULL;

	// first check for an ungraceful reconnect (client crashed, perhaps)
	for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) {

		const net_chan_t *ch = &cl->net_chan;

		if (cl->state == SV_CLIENT_FREE) // not in use, not interested
			continue;

		// the base address and either the qport or real port must match
		if (Net_CompareClientNetaddr(addr, &ch->remote_address)) {

			if (addr->port == ch->remote_address.port || qport == ch->qport) {
				client = cl;
				break;
			}
		}
	}

	// otherwise, treat as a fresh connect to a new slot
	if (!client) {
		for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) {
			if (cl->state == SV_CLIENT_FREE && !cl->edict->ai) { // we have a free one
				client = cl;
				break;
			}
		}
	}

	// no soup for you, next!!
	if (!client) {
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nServer is full\n");
		Com_Debug("Rejected a connection\n");
		return;
	}

	// give the game a chance to reject this connection or modify the user_info
	if (!(svs.game->ClientConnect(client->edict, user_info))) {
		const char *rejmsg = GetUserInfo(user_info, "rejmsg");

		if (strlen(rejmsg)) {
			Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\n%s\nConnection refused\n", rejmsg);
		} else {
			Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		}

		Com_Debug("Game rejected a connection\n");
		return;
	}

	// parse some info from the info strings
	g_strlcpy(client->user_info, user_info, sizeof(client->user_info));
	Sv_UserInfoChanged(client);

	// send the connect packet to the client
	Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "client_connect %s", sv_download_url->string);

	Netchan_Setup(NS_UDP_SERVER, &client->net_chan, addr, qport);

	Mem_InitBuffer(&client->datagram.buffer, client->datagram.data, sizeof(client->datagram.data));
	client->datagram.buffer.allow_overflow = true;

	client->last_message = svs.real_time; // don't timeout

	client->state = SV_CLIENT_CONNECTED;
}
Ejemplo n.º 2
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;
		}
	}
}