Esempio n. 1
0
/*
 * Svc_RemoteCommand
 *
 * A client issued an rcon command.  Shift down the remaining args and
 * redirect all output to the invoking client.
 */
static void Svc_RemoteCommand(void) {
	const boolean_t auth = Sv_RconAuthenticate();

	// first print to the server console
	if (auth)
		Com_Print("Rcon from %s:\n%s\n", Net_NetaddrToString(net_from),
				net_message.data + 4);
	else
		Com_Print("Bad rcon from %s:\n%s\n", Net_NetaddrToString(net_from),
				net_message.data + 4);

	// then redirect the remaining output back to the client
	Com_BeginRedirect(RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH,
			Sv_FlushRedirect);

	if (auth) {
		char remaining[MAX_STRING_CHARS];
		int i;

		remaining[0] = 0;

		for (i = 2; i < Cmd_Argc(); i++) {
			strcat(remaining, Cmd_Argv(i));
			strcat(remaining, " ");
		}

		Cmd_ExecuteString(remaining);
	} else {
		Com_Print("Bad rcon_password.\n");
	}

	Com_EndRedirect();
}
Esempio n. 2
0
/*
 * Sv_ConnectionlessPacket
 *
 * A connectionless packet has four leading 0xff bytes to distinguish it from
 * a game channel.  Clients that are in the game can still send these, and they
 * will be handled here.
 */
static void Sv_ConnectionlessPacket(void) {
	char *s;
	char *c;

	Msg_BeginReading(&net_message);
	Msg_ReadLong(&net_message); // skip the -1 marker

	s = Msg_ReadStringLine(&net_message);

	Cmd_TokenizeString(s);

	c = Cmd_Argv(0);
	Com_Debug("Packet from %s: %s\n", Net_NetaddrToString(net_from), c);

	if (!strcmp(c, "ping"))
		Svc_Ping();
	else if (!strcmp(c, "ack"))
		Svc_Ack();
	else if (!strcmp(c, "status"))
		Svc_Status();
	else if (!strcmp(c, "info"))
		Svc_Info();
	else if (!strcmp(c, "get_challenge"))
		Svc_GetChallenge();
	else if (!strcmp(c, "connect"))
		Svc_Connect();
	else if (!strcmp(c, "rcon"))
		Svc_RemoteCommand();
	else
		Com_Print("Bad connectionless packet from %s:\n%s\n",
				Net_NetaddrToString(net_from), s);
}
Esempio n. 3
0
/*
 * @brief A connection-less packet has four leading 0xff bytes to distinguish
 * it from a game channel. Clients that are in the game can still send these,
 * and they will be handled here.
 */
static void Sv_ConnectionlessPacket(void) {

	Net_BeginReading(&net_message);
	Net_ReadLong(&net_message); // skip the -1 marker

	const char *s = Net_ReadStringLine(&net_message);

	Cmd_TokenizeString(s);

	const char *c = Cmd_Argv(0);
	const char *a = Net_NetaddrToString(&net_from);

	Com_Debug("Packet from %s: %s\n", a, c);

	if (!g_strcmp0(c, "ping"))
		Sv_Ping_f();
	else if (!g_strcmp0(c, "ack"))
		Sv_Ack_f();
	else if (!g_strcmp0(c, "status"))
		Sv_Status_f();
	else if (!g_strcmp0(c, "info"))
		Sv_Info_f();
	else if (!g_strcmp0(c, "get_challenge"))
		Sv_GetChallenge_f();
	else if (!g_strcmp0(c, "connect"))
		Sv_Connect_f();
	else if (!g_strcmp0(c, "rcon"))
		Sv_Rcon_f();
	else
		Com_Print("Bad connectionless packet from %s:\n%s\n", a, s);
}
Esempio n. 4
0
/**
 * @brief
 */
void Cl_Ping_f(void) {
	net_addr_t addr;
	cl_server_info_t *server;

	if (Cmd_Argc() != 2) {
		Com_Print("Usage: %s <address>\n", Cmd_Argv(0));
		return;
	}

	server = NULL;

	if (!Net_StringToNetaddr(Cmd_Argv(1), &addr)) {
		Com_Print("Invalid address\n");
		return;
	}

	if (!addr.port) { // use default
		addr.port = (uint16_t) htons(PORT_SERVER);
	}

	server = Cl_ServerForNetaddr(&addr);

	if (!server) { // add it
		server = Cl_AddServer(&addr);
		server->source = SERVER_SOURCE_USER;
	}

	server->ping_time = quetoo.ticks;
	server->ping = 999;

	Com_Print("Pinging %s\n", Net_NetaddrToString(&server->addr));

	Netchan_OutOfBandPrint(NS_UDP_CLIENT, &server->addr, "info %i", PROTOCOL_MAJOR);
}
Esempio n. 5
0
/*
 * @brief
 */
void Net_SendPacket(net_src_t source, size_t size, void *data, net_addr_t to) {
	struct sockaddr_in to_addr;
	int32_t sock, ret;

	if (to.type == NA_LOCAL) {
		Net_SendLocalPacket(source, size, data);
		return;
	}

	if (to.type == NA_IP_BROADCAST) {
		sock = ip_sockets[source];
		if (!sock)
			return;
	} else if (to.type == NA_IP) {
		sock = ip_sockets[source];
		if (!sock)
			return;
	} else {
		Com_Error(ERR_DROP, "Bad address type\n");
		return;
	}

	Net_NetAddrToSockaddr(&to, &to_addr);

	ret = sendto(sock, data, size, 0, (struct sockaddr *) &to_addr, sizeof(to_addr));

	if (ret == -1)
		Com_Warn("%s to %s.\n", Net_ErrorString(), Net_NetaddrToString(to));
}
Esempio n. 6
0
/*
 * @brief Sends heartbeat messages to master servers every 300s.
 */
void Sv_HeartbeatMasters(void) {
	const char *string;
	int32_t i;

	if (!dedicated->value)
		return; // only dedicated servers report to masters

	if (!sv_public->value)
		return; // a private dedicated game

	if (!svs.initialized) // we're not up yet
		return;

	if (svs.next_heartbeat > quetoo.time)
		return; // not time to send yet

	svs.next_heartbeat = quetoo.time + HEARTBEAT_SECONDS * 1000;

	// send the same string that we would give for a status command
	string = Sv_StatusString();

	// send to each master server
	for (i = 0; i < MAX_MASTERS; i++) {
		if (svs.masters[i].port) {
			Com_Print("Sending heartbeat to %s\n", Net_NetaddrToString(&svs.masters[i]));
			Netchan_OutOfBandPrint(NS_UDP_SERVER, &svs.masters[i], "heartbeat\n%s", string);
		}
	}
}
Esempio n. 7
0
/*
 * Cl_Ping_f
 */
void Cl_Ping_f(void) {
	net_addr_t addr;
	cl_server_info_t *server;

	if (Cmd_Argc() != 2) {
		Com_Print("Usage: %s <address>\n", Cmd_Argv(0));
		return;
	}

	server = NULL;

	if (!Net_StringToNetaddr(Cmd_Argv(1), &addr)) {
		Com_Print("Invalid address\n");
		return;
	}

	if (!addr.port) // use default
		addr.port = (unsigned short) BigShort(PORT_SERVER);

	server = Cl_ServerForNetaddr(&addr);

	if (!server) { // add it
		server = Cl_AddServer(&addr);
		server->source = SERVER_SOURCE_USER;
	}

	server->ping_time = cls.real_time;
	server->ping = 0;

	Com_Print("Pinging %s\n", Net_NetaddrToString(server->addr));

	Netchan_OutOfBandPrint(NS_CLIENT, server->addr, "info %i", PROTOCOL);
}
Esempio n. 8
0
/**
 * @brief
 */
static cl_server_info_t *Cl_AddServer(const net_addr_t *addr) {
	cl_server_info_t *s;

	s = (cl_server_info_t *) Mem_TagMalloc(sizeof(*s), MEM_TAG_CLIENT);

	s->addr = *addr;
	g_strlcpy(s->hostname, Net_NetaddrToString(&s->addr), sizeof(s->hostname));

	cls.servers = g_list_prepend(cls.servers, s);

	return s;
}
Esempio n. 9
0
/*
 * @brief
 */
_Bool Net_GetPacket(net_src_t source, net_addr_t *from, size_buf_t *message) {
	ssize_t ret;
	int32_t err;
	struct sockaddr_in from_addr;
	socklen_t from_len;
	char *s;

	if (Net_GetLocalPacket(source, from, message))
		return true;

	if (!ip_sockets[source])
		return false;

	from_len = sizeof(from_addr);

	ret = recvfrom(ip_sockets[source], (void *) message->data, message->max_size, 0,
			(struct sockaddr *) &from_addr, &from_len);

	Net_SockaddrToNetaddr(&from_addr, from);

	if (ret == -1) {
		err = Net_GetError();

		if (err == EWOULDBLOCK || err == ECONNREFUSED)
			return false; // not terribly abnormal

		s = source == NS_SERVER ? "server" : "client";
		Com_Warn("%s: %s from %s\n", s, Net_ErrorString(),
				Net_NetaddrToString(*from));
		return false;
	}

	if (ret == ((ssize_t) message->max_size)) {
		Com_Warn("Oversized packet from %s\n", Net_NetaddrToString(*from));
		return false;
	}

	message->size = (uint32_t) ret;
	return true;
}
Esempio n. 10
0
/*
 * Cl_AddServer
 */
static cl_server_info_t *Cl_AddServer(const net_addr_t *addr) {
	cl_server_info_t *s;

	s = (cl_server_info_t *) Z_Malloc(sizeof(*s));

	s->next = cls.servers;
	cls.servers = s;

	s->addr = *addr;
	strcpy(s->hostname, Net_NetaddrToString(s->addr));

	return s;
}
Esempio n. 11
0
/**
 * @brief
 */
void Cl_Servers_List_f(void) {
	char string[256];

	GList *e = cls.servers;

	while (e) {
		const cl_server_info_t *s = (cl_server_info_t *) e->data;

		g_snprintf(string, sizeof(string), "%-40.40s %-20.20s %-16.16s %-24.24s %02d/%02d %5dms",
		           s->hostname, Net_NetaddrToString(&s->addr), s->name, s->gameplay, s->clients,
		           s->max_clients, s->ping);
		Com_Print("%s\n", string);
		e = e->next;
	}
}
Esempio n. 12
0
/*
 * @brief
 */
static void Sv_ReadPackets(void) {

	while (Net_ReceiveDatagram(NS_UDP_SERVER, &net_from, &net_message)) {

		// check for connectionless packet (0xffffffff) first
		if (*(uint32_t *) net_message.data == 0xffffffff) {
			Sv_ConnectionlessPacket();
			continue;
		}

		// read the qport out of the message so we can fix up
		// stupid address translating routers
		Net_BeginReading(&net_message);

		Net_ReadLong(&net_message); // sequence number
		Net_ReadLong(&net_message); // sequence number

		const byte qport = Net_ReadByte(&net_message) & 0xff;

		// check for packets from connected clients
		sv_client_t * cl = svs.clients;
		for (int32_t i = 0; i < sv_max_clients->integer; i++, cl++) {

			if (cl->state == SV_CLIENT_FREE)
				continue;

			if (!Net_CompareClientNetaddr(&net_from, &cl->net_chan.remote_address))
				continue;

			if (cl->net_chan.qport != qport)
				continue;

			if (cl->net_chan.remote_address.port != net_from.port) {
				cl->net_chan.remote_address.port = net_from.port;
				Com_Warn("Fixed translated port for %s\n", Net_NetaddrToString(&net_from));
			}

			// this is a valid, sequenced packet, so process it
			if (Netchan_Process(&cl->net_chan, &net_message)) {
				cl->last_message = quetoo.time; // nudge timeout
				Sv_ParseClientMessage(cl);
			}

			// we've processed the packet for the correct client, so break
			break;
		}
	}
}
Esempio n. 13
0
/*
 * @brief Informs master servers that this server is halting.
 */
void Sv_ShutdownMasters(void) {
	int32_t i;

	if (!dedicated->value)
		return; // only dedicated servers send heartbeats

	if (!sv_public->value)
		return; // a private dedicated game

	// send to group master
	for (i = 0; i < MAX_MASTERS; i++) {
		if (svs.masters[i].port) {
			Com_Print("Sending shutdown to %s\n", Net_NetaddrToString(&svs.masters[i]));
			Netchan_OutOfBandPrint(NS_UDP_SERVER, &svs.masters[i], "shutdown");
		}
	}
}
Esempio n. 14
0
/*
 * @brief Re-send a connect message if the last one has timed out.
 */
static void Cl_CheckForResend(void) {

	// if the local server is running and we aren't then connect
	if (Com_WasInit(QUETOO_SERVER) && g_strcmp0(cls.server_name, "localhost")) {

		if (cls.state > CL_DISCONNECTED) {
			Cl_Disconnect();
		}

		g_strlcpy(cls.server_name, "localhost", sizeof(cls.server_name));

		cls.state = CL_CONNECTING;
		cls.connect_time = 0;
	}

	// re-send if we haven't received a reply yet
	if (cls.state != CL_CONNECTING)
		return;

	// don't flood connection packets
	if (cls.connect_time && (quetoo.time - cls.connect_time < 1000))
		return;

	net_addr_t addr;

	if (!Net_StringToNetaddr(cls.server_name, &addr)) {
		Com_Print("Bad server address\n");
		cls.state = CL_DISCONNECTED;
		return;
	}

	if (addr.port == 0)
		addr.port = htons(PORT_SERVER);

	cls.connect_time = quetoo.time; // for retransmit requests

	const char *s = Net_NetaddrToString(&addr);
	if (g_strcmp0(cls.server_name, s)) {
		Com_Print("Connecting to %s (%s)...\n", cls.server_name, s);
	} else {
		Com_Print("Connecting to %s...\n", cls.server_name);
	}

	Netchan_OutOfBandPrint(NS_UDP_CLIENT, &addr, "get_challenge\n");
}
Esempio n. 15
0
/**
 * @brief
 */
void Cl_ParseServerInfo(void) {
	char info[MAX_MSG_SIZE];

	cl_server_info_t *server = Cl_ServerForNetaddr(&net_from);
	if (!server) { // unknown server, assumed response to broadcast

		server = Cl_AddServer(&net_from);

		server->source = SERVER_SOURCE_BCAST;
		server->ping_time = cls.broadcast_time;
	}

	// try to parse the info string
	g_strlcpy(info, Net_ReadString(&net_message), sizeof(info));
	if (sscanf(info, "%63c\\%31c\\%31c\\%hu\\%hu", server->hostname, server->name,
	           server->gameplay, &server->clients, &server->max_clients) != 5) {

		Com_Debug(DEBUG_CLIENT, "Failed to parse info \"%s\" for %s\n", info, Net_NetaddrToString(&server->addr));

		server->hostname[0] = '\0';
		server->name[0] = '\0';
		server->gameplay[0] = '\0';
		server->clients = 0;
		server->max_clients = 0;

		return;
	}

	g_strchomp(server->hostname);
	g_strchomp(server->name);
	g_strchomp(server->gameplay);

	server->hostname[sizeof(server->hostname) - 1] = '\0';
	server->name[sizeof(server->name) - 1] = '\0';
	server->gameplay[sizeof(server->name) - 1] = '\0';

	server->ping = Clamp(quetoo.ticks - server->ping_time, 1u, 999u);

	Ui_UpdateBindings();
}
Esempio n. 16
0
/*
 * Cl_ParseStatusMessage
 */
void Cl_ParseStatusMessage(void) {
	extern void Ui_NewServer(void);
	cl_server_info_t *server;
	char info[MAX_MSG_SIZE];

	server = Cl_ServerForNetaddr(&net_from);

	if (!server) { // unknown server, assumed response to broadcast

		server = Cl_AddServer(&net_from);

		server->source = SERVER_SOURCE_BCAST;
		server->ping_time = cls.broadcast_time;
	}

	// try to parse the info string
	strncpy(info, Msg_ReadString(&net_message), sizeof(info) - 1);
	info[sizeof(info) - 1] = '\0';
	if (sscanf(info, "%63c\\%31c\\%31c\\%hu\\%hu", server->hostname,
			server->name, server->gameplay, &server->clients,
			&server->max_clients) != 5) {

		strcpy(server->hostname, Net_NetaddrToString(server->addr));
		server->name[0] = '\0';
		server->gameplay[0] = '\0';
		server->clients = 0;
		server->max_clients = 0;
	}
	server->hostname[63] = '\0';
	server->name[31] = '\0';
	server->gameplay[31] = '\0';

	server->ping = cls.real_time - server->ping_time;

	if (server->ping > 1000) // clamp the ping
		server->ping = 999;

	Ui_NewServer();
}
Esempio n. 17
0
/*
 * @brief A client issued an rcon command. Shift down the remaining args and
 * redirect all output to the invoking client.
 */
static void Sv_Rcon_f(void) {

	const _Bool auth = Sv_RconAuthenticate();

	const char *addr = Net_NetaddrToString(&net_from);

	// first print to the server console
	if (auth)
		Com_Print("Rcon from %s:\n%s\n", addr, net_message.data + 4);
	else
		Com_Print("Bad rcon from %s:\n%s\n", addr, net_message.data + 4);

	// then redirect the remaining output back to the client

	console_t rcon = { .Append = Sv_Rcon_Print };
	sv_rcon_buffer[0] = '\0';

	Con_AddConsole(&rcon);

	if (auth) {
		char cmd[MAX_STRING_CHARS];
		cmd[0] = '\0';

		for (int32_t i = 2; i < Cmd_Argc(); i++) {
			g_strlcat(cmd, Cmd_Argv(i), sizeof(cmd));
			g_strlcat(cmd, " ", sizeof(cmd));
		}

		Cmd_ExecuteString(cmd);
	} else {
		Com_Print("Bad rcon_password\n");
	}

	Netchan_OutOfBandPrint(NS_UDP_SERVER, &net_from, "print\n%s", sv_rcon_buffer);

	Con_RemoveConsole(&rcon);
}
Esempio n. 18
0
/*
 * Sv_HeartbeatMasters
 *
 * Sends heartbeat messages to master servers every 300s.
 */
static void Sv_HeartbeatMasters(void) {
	const char *string;
	int i;

	if (!dedicated->value)
		return; // only dedicated servers report to masters

	if (!sv_public->value)
		return; // a private dedicated game

	if (!svs.initialized) // we're not up yet
		return;

	if (svs.last_heartbeat > svs.real_time) // catch wraps
		svs.last_heartbeat = svs.real_time;

	if (svs.last_heartbeat) { // if we've sent one, wait a while
		if (svs.real_time - svs.last_heartbeat < HEARTBEAT_SECONDS * 1000)
			return; // not time to send yet
	}

	svs.last_heartbeat = svs.real_time;

	// send the same string that we would give for a status command
	string = Sv_StatusString();

	// send to each master server
	for (i = 0; i < MAX_MASTERS; i++) {
		if (svs.masters[i].port) {
			Com_Print("Sending heartbeat to %s\n",
					Net_NetaddrToString(svs.masters[i]));
			Netchan_OutOfBandPrint(NS_SERVER, svs.masters[i], "heartbeat\n%s",
					string);
		}
	}
}
Esempio n. 19
0
/*
 * @brief Tries to send an unreliable message to a connection, and handles the
 * transmission / retransmission of the reliable messages.
 *
 * A 0 size will still generate a packet and deal with the reliable messages.
 */
void Netchan_Transmit(net_chan_t *chan, byte *data, size_t len) {
	mem_buf_t send;
	byte send_buffer[MAX_MSG_SIZE];

	// check for message overflow
	if (chan->message.overflowed) {
		chan->fatal_error = true;
		Com_Print("%s:Outgoing message overflow\n", Net_NetaddrToString(&chan->remote_address));
		return;
	}

	const _Bool send_reliable = Netchan_NeedReliable(chan);

	if (!chan->reliable_size && chan->message.size) {
		memcpy(chan->reliable_buffer, chan->message_buffer, chan->message.size);
		chan->reliable_size = chan->message.size;
		chan->message.size = 0;
		chan->reliable_sequence ^= 1;
	}

	// write the packet header
	Mem_InitBuffer(&send, send_buffer, sizeof(send_buffer));

	const uint32_t w1 = (chan->outgoing_sequence & ~(1 << 31)) | (send_reliable << 31);
	const uint32_t w2 = (chan->incoming_sequence & ~(1 << 31)) | (chan->incoming_reliable_sequence
			<< 31);

	chan->outgoing_sequence++;
	chan->last_sent = quake2world.time;

	Net_WriteLong(&send, w1);
	Net_WriteLong(&send, w2);

	// send the qport if we are a client
	if (chan->source == NS_UDP_CLIENT)
		Net_WriteByte(&send, chan->qport);

	// copy the reliable message to the packet first
	if (send_reliable) {
		Mem_WriteBuffer(&send, chan->reliable_buffer, chan->reliable_size);
		chan->last_reliable_sequence = chan->outgoing_sequence;
	}

	// add the unreliable part if space is available
	if (send.max_size - send.size >= len)
		Mem_WriteBuffer(&send, data, len);
	else
		Com_Warn("Netchan_Transmit: dumped unreliable\n");

	// send the datagram
	Net_SendDatagram(chan->source, &chan->remote_address, send.data, send.size);

	if (net_showpackets->value) {
		if (send_reliable)
			Com_Print("Send %u bytes: s=%i reliable=%i ack=%i rack=%i\n", (uint32_t) send.size,
					chan->outgoing_sequence - 1, chan->reliable_sequence, chan->incoming_sequence,
					chan->incoming_reliable_sequence);
		else
			Com_Print("Send %u bytes : s=%i ack=%i rack=%i\n", (uint32_t) send.size,
					chan->outgoing_sequence - 1, chan->incoming_sequence,
					chan->incoming_reliable_sequence);
	}
}
Esempio n. 20
0
/*
 * @brief A convenience function for printing out client addresses.
 */
const char *Sv_NetaddrToString(const sv_client_t *cl) {
	return Net_NetaddrToString(&cl->net_chan.remote_address);
}
Esempio n. 21
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;
}
Esempio n. 22
0
/*
 * @brief
 */
static void Svc_Ack(void) {
	Com_Print("Ping acknowledge from %s\n", Net_NetaddrToString(&net_from));
}
Esempio n. 23
0
/*
 * @brief Called when the current net_message is from remote_address
 * modifies net_message so that it points to the packet payload
 */
_Bool Netchan_Process(net_chan_t *chan, mem_buf_t *msg) {
	uint32_t sequence, sequence_ack;
	uint32_t reliable_ack, reliable_message;

	// get sequence numbers
	Net_BeginReading(msg);

	sequence = Net_ReadLong(msg);
	sequence_ack = Net_ReadLong(msg);

	// read the qport if we are a server
	if (chan->source == NS_UDP_SERVER)
		Net_ReadByte(msg);

	reliable_message = sequence >> 31;
	reliable_ack = sequence_ack >> 31;

	sequence &= ~(1 << 31);
	sequence_ack &= ~(1 << 31);

	if (net_showpackets->value) {
		if (reliable_message)
			Com_Print("Recv %u bytes: s=%i reliable=%i ack=%i rack=%i\n", (uint32_t) msg->size,
					sequence, chan->incoming_reliable_sequence ^ 1, sequence_ack, reliable_ack);
		else
			Com_Print("Recv %u bytes : s=%i ack=%i rack=%i\n", (uint32_t) msg->size, sequence,
					sequence_ack, reliable_ack);
	}

	// discard stale or duplicated packets
	if (sequence <= chan->incoming_sequence) {
		if (net_showdrop->value)
			Com_Print("%s:Out of order packet %i at %i\n",
					Net_NetaddrToString(&chan->remote_address), sequence, chan->incoming_sequence);
		return false;
	}

	// dropped packets don't keep the message from being used
	chan->dropped = sequence - (chan->incoming_sequence + 1);
	if (chan->dropped > 0) {
		if (net_showdrop->value)
			Com_Print("%s:Dropped %i packets at %i\n", Net_NetaddrToString(&chan->remote_address),
					chan->dropped, sequence);
	}

	// if the current outgoing reliable message has been acknowledged
	// clear the buffer to make way for the next
	if (reliable_ack == chan->reliable_sequence)
		chan->reliable_size = 0; // it has been received

	// if this message contains a reliable message, bump incoming_reliable_sequence
	chan->incoming_sequence = sequence;
	chan->incoming_acknowledged = sequence_ack;
	chan->incoming_reliable_acknowledged = reliable_ack;
	if (reliable_message) {
		chan->incoming_reliable_sequence ^= 1;
	}

	// the message can now be read from the current message pointer
	chan->last_received = quake2world.time;

	return true;
}
Esempio n. 24
0
/*
 * @brief Callback which connects to the server specified in data.
 */
static TW_CALL void Ui_Servers_Connect(void *data) {
	cl_server_info_t *s = (cl_server_info_t *) data;

	Cbuf_AddText(va("connect %s\n", Net_NetaddrToString(&s->addr)));
}