Esempio n. 1
0
/**
 * @brief Handles a connectionless message from a client
 * @sa NET_OOB_Printf
 * @param[out] stream The stream to write to
 * @param msg The message buffer to read the connectionless data from
 */
static void SV_ConnectionlessPacket (struct net_stream *stream, dbuffer *msg)
{
	const char *c;
	char s[512];
	char buf[256];

	NET_ReadStringLine(msg, s, sizeof(s));

	Cmd_TokenizeString(s, false);

	c = Cmd_Argv(0);
	Com_DPrintf(DEBUG_SERVER, "Packet : %s\n", c);

	if (Q_streq(c, "teaminfo"))
		SVC_TeamInfo(stream);
	else if (Q_streq(c, "info"))
		SVC_Info(stream);
	else if (Q_streq(c, "status"))
		SVC_Status(stream);
	else if (Q_streq(c, "connect"))
		SVC_DirectConnect(stream);
	else if (Q_streq(c, "rcon"))
		SVC_RemoteCommand(stream);
	else
		Com_Printf("Bad connectionless packet from %s:\n%s\n", NET_StreamPeerToName(stream, buf, sizeof(buf), true), s);
}
Esempio n. 2
0
/**
 * @brief A client issued an rcon command. Shift down the remaining args. Redirect all printfs
 */
static void SVC_RemoteCommand (struct net_stream *stream)
{
	char buf[256];
	const char *peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false);
	bool valid = Rcon_Validate(Cmd_Argv(1));

	if (!valid)
		Com_Printf("Bad rcon from %s:\n%s\n", peername, Cmd_Argv(1));
	else
		Com_Printf("Rcon from %s:\n%s\n", peername, Cmd_Argv(1));

	Com_BeginRedirect(stream, sv_outputbuf, SV_OUTPUTBUF_LENGTH);

	if (!valid)
		/* inform the client */
		Com_Printf("Bad rcon_password.\n");
	else {
		char remaining[1024] = "";
		int i;

		/* execute the rcon commands */
		for (i = 2; i < Cmd_Argc(); i++) {
			Q_strcat(remaining, Cmd_Argv(i), sizeof(remaining));
			Q_strcat(remaining, " ", sizeof(remaining));
		}

		/* execute the string */
		Cmd_ExecuteString(remaining);
	}

	Com_EndRedirect();
}
Esempio n. 3
0
/**
 * @brief Handles a connectionless message from a client
 * @sa NET_OOB_Printf
 * @param[out] stream The stream to write to
 * @param msg The message buffer to read the connectionless data from
 */
static void SV_ConnectionlessPacket (struct net_stream* stream, dbuffer* msg)
{
    char s[512];

    NET_ReadStringLine(msg, s, sizeof(s));
    Cmd_TokenizeString(s, false, false);

    const char* c = Cmd_Argv(0);
    Com_DPrintf(DEBUG_SERVER, "Packet : %s\n", c);

    if (Q_streq(c, SV_CMD_TEAMINFO)) {
        SVC_TeamInfo(stream);
    } else if (Q_streq(c, SV_CMD_INFO)) {
        SVC_Info(stream);
    } else if (Q_streq(c, SV_CMD_STATUS)) {
        SVC_Status(stream);
    } else if (Q_streq(c, SV_CMD_CONNECT)) {
        SVC_DirectConnect(stream);
    } else if (Q_streq(c, SV_CMD_RCON)) {
        SVC_RemoteCommand(stream);
    } else {
        char buf[256];
        Com_Printf("Bad connectionless packet from %s:\n%s\n", NET_StreamPeerToName(stream, buf, sizeof(buf), true), s);
    }
}
Esempio n. 4
0
/**
 * @brief Find or allocate a bucket for an address
 */
static leakyBucket_t* SVC_BucketForAddress (struct net_stream& address, int burst, int period)
{
    char node[64];
    NET_StreamPeerToName(&address, node, sizeof(node), false);

    const long hash = Com_HashKey(node, MAX_HASHES);
    const int now = Sys_Milliseconds();

    for (leakyBucket_t* bucket = bucketHashes[hash]; bucket; bucket = bucket->next) {
        if (!Q_streq(bucket->node, node))
            continue;

        return bucket;
    }

    for (int i = 0; i < MAX_BUCKETS; i++) {
        leakyBucket_t* bucket = &buckets[i];
        const int interval = now - bucket->lastTime;

        /* Reclaim expired buckets */
        if (bucket->lastTime > 0 && (interval > (burst * period) || interval < 0)) {
            if (bucket->prev != nullptr) {
                bucket->prev->next = bucket->next;
            } else {
                bucketHashes[bucket->hash] = bucket->next;
            }

            if (bucket->next != nullptr) {
                bucket->next->prev = bucket->prev;
            }

            OBJZERO(*bucket);
        }

        if (Q_strnull(bucket->node)) {
            Q_strncpyz(bucket->node, node, sizeof(bucket->node));
            bucket->lastTime = now;
            bucket->burst = 0;
            bucket->hash = hash;

            /* Add to the head of the relevant hash chain */
            bucket->next = bucketHashes[hash];
            if (bucketHashes[hash] != nullptr) {
                bucketHashes[hash]->prev = bucket;
            }

            bucket->prev = nullptr;
            bucketHashes[hash] = bucket;

            return bucket;
        }
    }

    /* Couldn't allocate a bucket for this address */
    return nullptr;
}
Esempio n. 5
0
File: net.cpp Progetto: ufoai/ufoai
static void NET_ShowStreams_f (void)
{
	char buf[256];
	int cnt = 0;

	for (int i = 0; i < MAX_STREAMS; i++) {
		if (streams[i] == nullptr)
			continue;
		Com_Printf("Steam %i is opened: %s on socket %i (closed: %i, finished: %i, outbound: " UFO_SIZE_T ", inbound: " UFO_SIZE_T ")\n", i,
			NET_StreamPeerToName(streams[i], buf, sizeof(buf), true),
			streams[i]->socket, streams[i]->closed, streams[i]->finished,
			dbuffer_len(streams[i]->outbound), dbuffer_len(streams[i]->inbound));
		cnt++;
	}
	Com_Printf("%i/%i streams opened\n", cnt, MAX_STREAMS);
}
Esempio n. 6
0
/**
 * @brief A client issued an rcon command. Shift down the remaining args. Redirect all printfs
 */
static void SVC_RemoteCommand (struct net_stream* stream)
{
    char buf[64];
    const char* peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false);

    /* Prevent using rcon as an amplifier and make dictionary attacks impractical */
    if (SVC_RateLimitAddress(*stream)) {
        Com_DPrintf(DEBUG_SERVER, "SVC_RemoteCommand: rate limit from %s exceeded, dropping request\n", peername);
        return;
    }

    const bool valid = Rcon_Validate(Cmd_Argv(1));
    if (!valid) {
        static leakyBucket_t bucket;
        /* Make DoS via rcon impractical */
        if (SVC_RateLimit(&bucket, 10, 1000)) {
            Com_DPrintf(DEBUG_SERVER, "SVC_RemoteCommand: rate limit exceeded, dropping request\n");
            return;
        }

        Com_Printf("Bad rcon from %s with password: '******'\n", peername, Cmd_Argv(1));
    } else {
        Com_Printf("Rcon from %s\n", peername);
    }

    static char sv_outputbuf[1024];
    Com_BeginRedirect(stream, sv_outputbuf, sizeof(sv_outputbuf));

    if (!valid) {
        /* inform the client */
        Com_Printf(BAD_RCON_PASSWORD);
    } else {
        char remaining[1024] = "";
        int i;

        /* execute the rcon commands */
        for (i = 2; i < Cmd_Argc(); i++) {
            Q_strcat(remaining, sizeof(remaining), "%s ", Cmd_Argv(i));
        }

        /* execute the string */
        Cmd_ExecuteString("%s", remaining);
    }

    Com_EndRedirect();
}
Esempio n. 7
0
/**
 * @brief Returns the numerical representation of a @c net_stream
 * @note Not thread safe!
 */
const char* NET_StreamToString (struct net_stream* s)
{
	static char node[64];
	NET_StreamPeerToName(s, node, sizeof(node), false);
	return node;
}
Esempio n. 8
0
/**
 * @sa Qcommon_Frame
 */
void NET_Wait (int timeout)
{
	struct timeval tv;
	int ready;
	int i;

	fd_set read_fds_out;
	fd_set write_fds_out;

	memcpy(&read_fds_out, &read_fds, sizeof(read_fds_out));
	memcpy(&write_fds_out, &write_fds, sizeof(write_fds_out));

	/* select() won't notice that loopback streams are ready, so we'll
	 * eliminate the delay directly */
	if (loopback_ready)
		timeout = 0;

	tv.tv_sec = timeout / 1000;
	tv.tv_usec = 1000 * (timeout % 1000);
#ifdef _WIN32
	if (read_fds_out.fd_count == 0) {
		Sys_Sleep(timeout);
		ready = 0;
	} else
#endif
		ready = select(maxfd, &read_fds_out, &write_fds_out, nullptr, &tv);

	if (ready == -1) {
		Com_Printf("select failed: %s\n", netStringError(netError));
		return;
	}

	if (ready == 0 && !loopback_ready)
		return;

	if (server_socket != INVALID_SOCKET && FD_ISSET(server_socket, &read_fds_out)) {
		const SOCKET client_socket = accept(server_socket, nullptr, 0);
		if (client_socket == INVALID_SOCKET) {
			if (errno != EAGAIN)
				Com_Printf("accept on socket %d failed: %s\n", server_socket, netStringError(netError));
		} else
			do_accept(client_socket);
	}

	for (i = 0; i < MAX_STREAMS; i++) {
		struct net_stream* s = streams[i];

		if (!s)
			continue;

		if (s->loopback) {
			/* If the peer is gone and the buffer is empty, close the stream */
			if (!s->loopback_peer && NET_StreamGetLength(s) == 0) {
				NET_StreamClose(s);
			}
			/* Note that s is potentially invalid after the callback returns - we'll close dead streams on the next pass */
			else if (s->ready && s->func) {
				s->func(s);
			}

			continue;
		}

		if (s->socket == INVALID_SOCKET)
			continue;

		if (FD_ISSET(s->socket, &write_fds_out)) {
			char buf[4096];
			int len;

			if (dbuffer_len(s->outbound) == 0) {
				FD_CLR(s->socket, &write_fds);

				/* Finished streams are closed when their outbound queues empty */
				if (s->finished)
					NET_StreamClose(s);

				continue;
			}

			{
				const ScopedMutex scopedMutex(netMutex);
				len = s->outbound->get(buf, sizeof(buf));
				len = send(s->socket, buf, len, 0);

				s->outbound->remove(len);
			}

			if (len < 0) {
				Com_Printf("write on socket %d failed: %s\n", s->socket, netStringError(netError));
				NET_StreamClose(s);
				continue;
			}

			Com_DPrintf(DEBUG_SERVER, "wrote %d bytes to stream %d (%s)\n", len, i, NET_StreamPeerToName(s, buf, sizeof(buf), true));
		}

		if (FD_ISSET(s->socket, &read_fds_out)) {
			char buf[4096];
			const int len = recv(s->socket, buf, sizeof(buf), 0);
			if (len <= 0) {
				if (len == -1)
					Com_Printf("read on socket %d failed: %s\n", s->socket, netStringError(netError));
				NET_StreamClose(s);
				continue;
			} else {
				if (s->inbound) {
					SDL_LockMutex(netMutex);
					s->inbound->add(buf, len);
					SDL_UnlockMutex(netMutex);

					Com_DPrintf(DEBUG_SERVER, "read %d bytes from stream %d (%s)\n", len, i, NET_StreamPeerToName(s, buf, sizeof(buf), true));

					/* Note that s is potentially invalid after the callback returns */
					if (s->func)
						s->func(s);

					continue;
				}
			}
		}
	}

	for (i = 0; i < MAX_DATAGRAM_SOCKETS; i++) {
		struct datagram_socket* s = datagram_sockets[i];

		if (!s)
			continue;

		if (FD_ISSET(s->socket, &write_fds_out)) {
			if (s->queue) {
				struct datagram* dgram = s->queue;
				const int len = sendto(s->socket, dgram->msg, dgram->len, 0, (struct sockaddr* )dgram->addr, s->addrlen);
				if (len == -1)
					Com_Printf("sendto on socket %d failed: %s\n", s->socket, netStringError(netError));
				/* Regardless of whether it worked, we don't retry datagrams */
				s->queue = dgram->next;
				Mem_Free(dgram->msg);
				Mem_Free(dgram->addr);
				Mem_Free(dgram);
				if (!s->queue)
					s->queue_tail = &s->queue;
			} else {
				FD_CLR(s->socket, &write_fds);
			}
		}

		if (FD_ISSET(s->socket, &read_fds_out)) {
			char buf[256];
			char addrbuf[256];
			socklen_t addrlen = sizeof(addrbuf);
			const int len = recvfrom(s->socket, buf, sizeof(buf), 0, (struct sockaddr* )addrbuf, &addrlen);
			if (len == -1)
				Com_Printf("recvfrom on socket %d failed: %s\n", s->socket, netStringError(netError));
			else
				s->func(s, buf, len, (struct sockaddr* )addrbuf);
		}
	}

	loopback_ready = false;
}
Esempio n. 9
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;
	bool connected;
	char buf[256];
	const char *peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false);

	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;

	{
		const ScopedMutex scopedMutex(svs.serverMutex);
		connected = svs.ge->ClientConnect(player, userinfo, sizeof(userinfo));
	}

	/* 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 = true;
	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);
}