#define DPMASTER_PORT 27950
// note this defaults on for dedicated servers, off for listen servers
-cvar_t sv_public = {0, "sv_public", "0", "advertises this server on the master server (so that players can find it in the server browser)"};
+cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect"};
static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
static cvar_t sv_masters [] =
static unsigned char net_message_buf[NET_MAXMESSAGE];
cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
-cvar_t net_messagerejointimeout = {0, "net_messagerejointimeout","10", "give a player this much time in seconds to rejoin and continue playing (not losing frags and such)"};
cvar_t net_connecttimeout = {0, "net_connecttimeout","10", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods)"};
+cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods)"};
cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
cvar_t net_address = {0, "net_address", "0.0.0.0", "network address to open ports on"};
//cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]", "network address to open ipv6 ports on"};
+char net_extresponse[NET_EXTRESPONSE_MAX][1400];
+int net_extresponse_count = 0;
+int net_extresponse_last = 0;
+
// ServerList interface
serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
{
if (conn->message.cursize > (int)sizeof(conn->sendMessage))
{
- Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, sizeof(conn->sendMessage));
+ Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
conn->message.overflowed = true;
return -1;
}
return conn;
}
+void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress);
void NetConn_Close(netconn_t *conn)
{
netconn_t *c;
// remove connection from list
+
+ // allow the client to reconnect immediately
+ NetConn_ClearConnectFlood(&(conn->peeraddress));
+
if (conn == netconn_list)
netconn_list = conn->next;
else
}
}
-static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol)
+static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout)
{
if (length < 8)
return 0;
+ // TODO: add netgraph stuff rather than just packetloss counting...
+
if (protocol == PROTOCOL_QUAKEWORLD)
{
int sequence, sequence_ack;
sequence_ack &= ~(1<<31);
if (sequence <= conn->qw.incoming_sequence)
{
- Con_DPrint("Got a stale datagram\n");
+ //Con_DPrint("Got a stale datagram\n");
return 0;
}
count = sequence - (conn->qw.incoming_sequence + 1);
if (count > 0)
{
droppedDatagrams += count;
- Con_DPrintf("Dropped %u datagram(s)\n", count);
+ //Con_DPrintf("Dropped %u datagram(s)\n", count);
+ while (count--)
+ {
+ conn->packetlost[conn->packetlostcounter] = true;
+ conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
+ }
}
+ conn->packetlost[conn->packetlostcounter] = false;
+ conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
if (reliable_ack == conn->qw.reliable_sequence)
{
// received, now we will be able to send another reliable message
if (reliable_message)
conn->qw.incoming_reliable_sequence ^= 1;
conn->lastMessageTime = realtime;
- conn->timeout = realtime + net_messagetimeout.value;
+ conn->timeout = realtime + newtimeout;
unreliableMessagesReceived++;
SZ_Clear(&net_message);
SZ_Write(&net_message, data, length);
{
count = sequence - conn->nq.unreliableReceiveSequence;
droppedDatagrams += count;
- Con_DPrintf("Dropped %u datagram(s)\n", count);
+ //Con_DPrintf("Dropped %u datagram(s)\n", count);
+ while (count--)
+ {
+ conn->packetlost[conn->packetlostcounter] = true;
+ conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
+ }
}
+ conn->packetlost[conn->packetlostcounter] = false;
+ conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
conn->nq.unreliableReceiveSequence = sequence + 1;
conn->lastMessageTime = realtime;
- conn->timeout = realtime + net_messagetimeout.value;
+ conn->timeout = realtime + newtimeout;
unreliableMessagesReceived++;
if (length > 0)
{
return 2;
}
}
- else
- Con_DPrint("Got a stale datagram\n");
+ //else
+ // Con_DPrint("Got a stale datagram\n");
return 1;
}
else if (flags & NETFLAG_ACK)
if (conn->nq.ackSequence != conn->nq.sendSequence)
Con_DPrint("ack sequencing error\n");
conn->lastMessageTime = realtime;
- conn->timeout = realtime + net_messagetimeout.value;
+ conn->timeout = realtime + newtimeout;
if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
{
unsigned int packetLen;
else
conn->sendMessageLength = 0;
}
- else
- Con_DPrint("Duplicate ACK received\n");
+ //else
+ // Con_DPrint("Duplicate ACK received\n");
}
- else
- Con_DPrint("Stale ACK received\n");
+ //else
+ // Con_DPrint("Stale ACK received\n");
return 1;
}
else if (flags & NETFLAG_DATA)
if (sequence == conn->nq.receiveSequence)
{
conn->lastMessageTime = realtime;
- conn->timeout = realtime + net_messagetimeout.value;
+ conn->timeout = realtime + newtimeout;
conn->nq.receiveSequence++;
if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
cls.protocol = initialprotocol;
if (cls.protocol == PROTOCOL_QUAKEWORLD)
Cmd_ForwardStringToServer("new");
+ if (cls.protocol == PROTOCOL_QUAKE)
+ {
+ // write a keepalive (clc_nop) as it seems to greatly improve the
+ // chances of connecting to a netquake server
+ sizebuf_t msg;
+ unsigned char buf[4];
+ memset(&msg, 0, sizeof(msg));
+ msg.data = buf;
+ msg.maxsize = sizeof(buf);
+ MSG_WriteChar(&msg, clc_nop);
+ NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol);
+ }
}
int NetConn_IsLocalGame(void)
serverlist_querywaittime = realtime + 3;
return true;
}
+ if (!strncmp(string, "extResponse ", 12))
+ {
+ ++net_extresponse_count;
+ if(net_extresponse_count > NET_EXTRESPONSE_MAX)
+ net_extresponse_count = NET_EXTRESPONSE_MAX;
+ net_extresponse_last = (net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
+ dpsnprintf(net_extresponse[net_extresponse_last], sizeof(net_extresponse[net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
+ return true;
+ }
if (!strncmp(string, "ping", 4))
{
if (developer.integer >= 10)
return true;
}
// quakeworld ingame packet
- if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol)) == 2)
+ if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
{
ret = 0;
CL_ParseServerMessage();
length -= 4;
LHNETADDRESS_SetPort(&clientportaddress, port);
}
- M_Update_Return_Reason("Accepted");
// update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
+ M_Update_Return_Reason("Accepted");
NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
}
break;
cls.connect_trying = false;
M_Update_Return_Reason((char *)data);
break;
- // TODO: fix this code so that lan searches for quake servers will work
case CCREP_SERVER_INFO:
if (developer.integer >= 10)
Con_Printf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
return true;
}
ret = 0;
- if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol)) == 2)
+ if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
CL_ParseServerMessage();
return ret;
}
client_t *cl = &svs.clients[i];
if (cl->active)
{
- int nameind, cleanind;
+ int nameind, cleanind, pingvalue;
char curchar;
char cleanname [sizeof(cl->name)];
}
} while (curchar != '\0');
+ pingvalue = (int)(cl->ping * 1000.0f);
+ if(cl->netconnection)
+ pingvalue = bound(1, pingvalue, 9999);
+ else
+ pingvalue = 0;
length = dpsnprintf(ptr, left, "%d %d \"%s\"\n",
cl->frags,
- (int)(cl->ping * 1000.0f),
+ pingvalue,
cleanname);
if(length < 0)
return false;
return true;
}
+static qboolean NetConn_PreventConnectFlood(lhnetaddress_t *peeraddress)
+{
+ int floodslotnum, bestfloodslotnum;
+ double bestfloodtime;
+ lhnetaddress_t noportpeeraddress;
+ // see if this is a connect flood
+ noportpeeraddress = *peeraddress;
+ LHNETADDRESS_SetPort(&noportpeeraddress, 0);
+ bestfloodslotnum = 0;
+ bestfloodtime = sv.connectfloodaddresses[bestfloodslotnum].lasttime;
+ for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
+ {
+ if (bestfloodtime >= sv.connectfloodaddresses[floodslotnum].lasttime)
+ {
+ bestfloodtime = sv.connectfloodaddresses[floodslotnum].lasttime;
+ bestfloodslotnum = floodslotnum;
+ }
+ if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
+ {
+ // this address matches an ongoing flood address
+ if (realtime < sv.connectfloodaddresses[floodslotnum].lasttime + net_connectfloodblockingtimeout.value)
+ {
+ // renew the ban on this address so it does not expire
+ // until the flood has subsided
+ sv.connectfloodaddresses[floodslotnum].lasttime = realtime;
+ //Con_Printf("Flood detected!\n");
+ return true;
+ }
+ // the flood appears to have subsided, so allow this
+ bestfloodslotnum = floodslotnum; // reuse the same slot
+ break;
+ }
+ }
+ // begin a new timeout on this address
+ sv.connectfloodaddresses[bestfloodslotnum].address = noportpeeraddress;
+ sv.connectfloodaddresses[bestfloodslotnum].lasttime = realtime;
+ //Con_Printf("Flood detection initiated!\n");
+ return false;
+}
+
+void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
+{
+ int floodslotnum;
+ lhnetaddress_t noportpeeraddress;
+ // see if this is a connect flood
+ noportpeeraddress = *peeraddress;
+ LHNETADDRESS_SetPort(&noportpeeraddress, 0);
+ for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
+ {
+ if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
+ {
+ // this address matches an ongoing flood address
+ // remove the ban
+ sv.connectfloodaddresses[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
+ sv.connectfloodaddresses[floodslotnum].lasttime = 0;
+ //Con_Printf("Flood cleared!\n");
+ }
+ }
+}
+
extern void SV_SendServerinfo (client_t *client);
static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
{
int i, ret, clientnum, best;
double besttime;
client_t *client;
- netconn_t *conn;
char *s, *string, response[1400], addressstring2[128], stringbuf[16384];
if (!sv.active)
Com_HexDumpToConsole(data, length);
}
- if (length >= 12 && !memcmp(string, "getchallenge", 12))
+ if (length >= 12 && !memcmp(string, "getchallenge", 12) && sv_public.integer > -2)
{
for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
{
NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
return true;
}
- if (length > 8 && !memcmp(string, "connect\\", 8))
+ if (length > 8 && !memcmp(string, "connect\\", 8) && sv_public.integer > -2)
{
string += 7;
length -= 7;
- if ((s = SearchInfostring(string, "challenge")))
+
+ if (!(s = SearchInfostring(string, "challenge")))
+ return true;
+ // validate the challenge
+ for (i = 0;i < MAX_CHALLENGES;i++)
+ if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
+ break;
+ // if the challenge is not recognized, drop the packet
+ if (i == MAX_CHALLENGES)
+ return true;
+
+ // check engine protocol
+ if (strcmp(SearchInfostring(string, "protocol"), "darkplaces 3"))
{
- // validate the challenge
- for (i = 0;i < MAX_CHALLENGES;i++)
- if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
- break;
- if (i < MAX_CHALLENGES)
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
+ NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
+ return true;
+ }
+
+ // see if this is a duplicate connection request or a disconnected
+ // client who is rejoining to the same client slot
+ for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ {
+ if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
{
- // check engine protocol
- if (strcmp(SearchInfostring(string, "protocol"), "darkplaces 3"))
+ // this is a known client...
+ if (client->spawned)
{
+ // client crashed and is coming back,
+ // keep their stuff intact
if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
- NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
+ Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
+ NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
+ SV_VM_Begin();
+ SV_SendServerinfo(client);
+ SV_VM_End();
}
else
{
- // see if this is a duplicate connection request
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
- break;
- if (clientnum < svs.maxclients && realtime - client->connecttime < net_messagerejointimeout.value)
- {
- // client is still trying to connect,
- // so we send a duplicate reply
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
- NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
- }
-#if 0
- else if (clientnum < svs.maxclients)
- {
- if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value)
- {
- // client crashed and is coming back, keep their stuff intact
- SV_SendServerinfo(client);
- //host_client = client;
- //SV_DropClient (true);
- }
- // else ignore them
- }
-#endif
- else
- {
- // this is a new client, find a slot
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (!client->active)
- break;
- if (clientnum < svs.maxclients)
- {
- // prepare the client struct
- if ((conn = NetConn_Open(mysocket, peeraddress)))
- {
- // allocated connection
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
- NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
- // now set up the client
- SV_VM_Begin();
- SV_ConnectClient(clientnum, conn);
- SV_VM_End();
- NetConn_Heartbeat(1);
- }
- }
- else
- {
- // server is full
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
- NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
- }
- }
+ // client is still trying to connect,
+ // so we send a duplicate reply
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
+ NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
}
+ return true;
}
}
+
+ if (NetConn_PreventConnectFlood(peeraddress))
+ return true;
+
+ // find an empty client slot for this new client
+ for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ {
+ netconn_t *conn;
+ if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
+ {
+ // allocated connection
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
+ NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
+ // now set up the client
+ SV_VM_Begin();
+ SV_ConnectClient(clientnum, conn);
+ SV_VM_End();
+ NetConn_Heartbeat(1);
+ return true;
+ }
+ }
+
+ // no empty slots found - server is full
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
+ NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
+
return true;
}
- if (length >= 7 && !memcmp(string, "getinfo", 7))
+ if (length >= 7 && !memcmp(string, "getinfo", 7) && sv_public.integer > -1)
{
const char *challenge = NULL;
}
return true;
}
- if (length >= 9 && !memcmp(string, "getstatus", 9))
+ if (length >= 9 && !memcmp(string, "getstatus", 9) && sv_public.integer > -1)
{
const char *challenge = NULL;
// we're done processing this packet now
return true;
}
- // LordHavoc: disabled netquake control packet support in server
-#if 0
+ // netquake control packets, supported for compatibility only, and only
+ // when running game protocols that are normally served via this connection
+ // protocol
+ // (this protects more modern protocols against being used for
+ // Quake packet flood Denial Of Service attacks)
+ if (length >= 5 && (i = BigLong(*((int *)data))) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3))
{
- int c, control;
- // netquake control packets, supported for compatibility only
- if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
+ int c = data[4];
+ data += 5;
+ length -= 5;
+ switch (c)
{
- c = data[4];
- data += 5;
- length -= 5;
- switch (c)
+ case CCREQ_CONNECT:
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
+ if (length < (int)strlen("QUAKE") + 1 + 1)
+ break;
+ if(sv_public.integer <= -2)
+ break;
+
+ if (memcmp(data, "QUAKE", strlen("QUAKE") + 1) != 0 || (int)data[strlen("QUAKE") + 1] != NET_PROTOCOL_VERSION)
{
- case CCREQ_CONNECT:
if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
- if (length >= (int)strlen("QUAKE") + 1 + 1)
+ Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_REJECT);
+ MSG_WriteString(&net_message, "Incompatible version.\n");
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
+ SZ_Clear(&net_message);
+ break;
+ }
+
+ // see if this connect request comes from a known client
+ for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ {
+ if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
{
- if (memcmp(data, "QUAKE", strlen("QUAKE") + 1) != 0 || (int)data[strlen("QUAKE") + 1] != NET_PROTOCOL_VERSION)
+ // this is either a duplicate connection request
+ // or coming back from a timeout
+ // (if so, keep their stuff intact)
+
+ // send a reply
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_ACCEPT);
+ MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
+ SZ_Clear(&net_message);
+
+ // if client is already spawned, re-send the
+ // serverinfo message as they'll need it to play
+ if (client->spawned)
{
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
- SZ_Clear(&net_message);
- // save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_REJECT);
- MSG_WriteString(&net_message, "Incompatible version.\n");
- *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
- NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
- SZ_Clear(&net_message);
- }
- else
- {
- // see if this is a duplicate connection request
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
- break;
- if (clientnum < svs.maxclients)
- {
- // duplicate connection request
- if (realtime - client->connecttime < 2.0)
- {
- // client is still trying to connect,
- // so we send a duplicate reply
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
- SZ_Clear(&net_message);
- // save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_ACCEPT);
- MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
- *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
- NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
- SZ_Clear(&net_message);
- }
-#if 0
- else if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value)
- {
- SV_SendServerinfo(client);
- // the old client hasn't sent us anything
- // in quite a while, so kick off and let
- // the retry take care of it...
- //host_client = client;
- //SV_DropClient (true);
- }
-#endif
- }
- else
- {
- // this is a new client, find a slot
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (!client->active)
- break;
- if (clientnum < svs.maxclients && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
- {
- // connect to the client
- // everything is allocated, just fill in the details
- strlcpy (conn->address, addressstring2, sizeof (conn->address));
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
- // send back the info about the server connection
- SZ_Clear(&net_message);
- // save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_ACCEPT);
- MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
- *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
- NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
- SZ_Clear(&net_message);
- // now set up the client struct
- SV_VM_Begin();
- SV_ConnectClient(clientnum, conn);
- SV_VM_End();
- NetConn_Heartbeat(1);
- }
- else
- {
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
- // no room; try to let player know
- SZ_Clear(&net_message);
- // save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_REJECT);
- MSG_WriteString(&net_message, "Server is full.\n");
- *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
- NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
- SZ_Clear(&net_message);
- }
- }
+ SV_VM_Begin();
+ SV_SendServerinfo(client);
+ SV_VM_End();
}
+ return true;
}
+ }
+
+ // this is a new client, check for connection flood
+ if (NetConn_PreventConnectFlood(peeraddress))
break;
-#if 0
- case CCREQ_SERVER_INFO:
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
- if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
+
+ // find a slot for the new client
+ for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ {
+ netconn_t *conn;
+ if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
{
+ // connect to the client
+ // everything is allocated, just fill in the details
+ strlcpy (conn->address, addressstring2, sizeof (conn->address));
if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
+ Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
+ // send back the info about the server connection
SZ_Clear(&net_message);
// save space for the header, filled in later
MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
- UDP_GetSocketAddr(UDP_acceptSock, &newaddr);
- MSG_WriteString(&net_message, UDP_AddrToString(&newaddr));
- MSG_WriteString(&net_message, hostname.string);
- MSG_WriteString(&net_message, sv.name);
- MSG_WriteByte(&net_message, net_activeconnections);
- MSG_WriteByte(&net_message, svs.maxclients);
- MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+ MSG_WriteByte(&net_message, CCREP_ACCEPT);
+ MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
SZ_Clear(&net_message);
+ // now set up the client struct
+ SV_VM_Begin();
+ SV_ConnectClient(clientnum, conn);
+ SV_VM_End();
+ NetConn_Heartbeat(1);
+ return true;
}
- break;
- case CCREQ_PLAYER_INFO:
- if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
- if (sv.active)
- {
- int playerNumber, activeNumber, clientNumber;
- client_t *client;
+ }
- playerNumber = MSG_ReadByte();
- activeNumber = -1;
- for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
- if (client->active && ++activeNumber == playerNumber)
- break;
- if (clientNumber != svs.maxclients)
- {
- SZ_Clear(&net_message);
- // save space for the header, filled in later
- MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
- MSG_WriteByte(&net_message, playerNumber);
- MSG_WriteString(&net_message, client->name);
- MSG_WriteLong(&net_message, client->colors);
- MSG_WriteLong(&net_message, (int)client->edict->fields.server->frags);
- MSG_WriteLong(&net_message, (int)(realtime - client->connecttime));
- MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
- *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
- NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
- SZ_Clear(&net_message);
- }
- }
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
+ // no room; try to let player know
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_REJECT);
+ MSG_WriteString(&net_message, "Server is full.\n");
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
+ SZ_Clear(&net_message);
+ break;
+ case CCREQ_SERVER_INFO:
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
+ if(sv_public.integer <= -1)
break;
- case CCREQ_RULE_INFO:
+ if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
+ {
+ int numclients;
+ char myaddressstring[128];
if (developer.integer >= 10)
- Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
- if (sv.active)
- {
- char *prevCvarName;
- cvar_t *var;
-
- // find the search start location
- prevCvarName = MSG_ReadString();
- var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
+ Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
+ LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
+ MSG_WriteString(&net_message, myaddressstring);
+ MSG_WriteString(&net_message, hostname.string);
+ MSG_WriteString(&net_message, sv.name);
+ // How many clients are there?
+ for (i = 0, numclients = 0;i < svs.maxclients;i++)
+ if (svs.clients[i].active)
+ numclients++;
+ MSG_WriteByte(&net_message, numclients);
+ MSG_WriteByte(&net_message, svs.maxclients);
+ MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
+ SZ_Clear(&net_message);
+ }
+ break;
+ case CCREQ_PLAYER_INFO:
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
+ if(sv_public.integer <= -1)
+ break;
+ if (sv.active)
+ {
+ int playerNumber, activeNumber, clientNumber;
+ client_t *client;
- // send the response
+ playerNumber = MSG_ReadByte();
+ activeNumber = -1;
+ for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
+ if (client->active && ++activeNumber == playerNumber)
+ break;
+ if (clientNumber != svs.maxclients)
+ {
SZ_Clear(&net_message);
// save space for the header, filled in later
MSG_WriteLong(&net_message, 0);
- MSG_WriteByte(&net_message, CCREP_RULE_INFO);
- if (var)
- {
- MSG_WriteString(&net_message, var->name);
- MSG_WriteString(&net_message, var->string);
- }
+ MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
+ MSG_WriteByte(&net_message, playerNumber);
+ MSG_WriteString(&net_message, client->name);
+ MSG_WriteLong(&net_message, client->colors);
+ MSG_WriteLong(&net_message, (int)client->edict->fields.server->frags);
+ MSG_WriteLong(&net_message, (int)(realtime - client->connecttime));
+ MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
SZ_Clear(&net_message);
}
+ }
+ break;
+ case CCREQ_RULE_INFO:
+ if (developer.integer >= 10)
+ Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
+ if(sv_public.integer <= -1)
break;
-#endif
- default:
- break;
+ if (sv.active)
+ {
+ char *prevCvarName;
+ cvar_t *var;
+
+ // find the search start location
+ prevCvarName = MSG_ReadString();
+ var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
+
+ // send the response
+ SZ_Clear(&net_message);
+ // save space for the header, filled in later
+ MSG_WriteLong(&net_message, 0);
+ MSG_WriteByte(&net_message, CCREP_RULE_INFO);
+ if (var)
+ {
+ MSG_WriteString(&net_message, var->name);
+ MSG_WriteString(&net_message, var->string);
+ }
+ *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+ NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
+ SZ_Clear(&net_message);
}
- // we may not have liked the packet, but it was a valid control
- // packet, so we're done processing this packet now
- return true;
+ break;
+ default:
+ break;
}
+ // we may not have liked the packet, but it was a valid control
+ // packet, so we're done processing this packet now
+ return true;
}
-#endif
if (host_client)
{
- if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol)) == 2)
+ if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->spawned ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
{
SV_VM_Begin();
SV_ReadClientMessage();
// search internet
for (masternum = 0;sv_masters[masternum].name;masternum++)
{
- if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
+ if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
{
masterquerycount++;
NetConn_WriteString(cl_sockets[i], request, &masteraddress);
// make advertising optional and don't advertise singleplayer games, and
// only send a heartbeat as often as the admin wants
- if (sv.active && sv_public.integer && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
+ if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
{
nextheartbeattime = realtime + sv_heartbeatperiod.value;
for (masternum = 0;sv_masters[masternum].name;masternum++)
- if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
+ if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
}
}
Cvar_RegisterVariable(&net_slist_timeout);
Cvar_RegisterVariable(&net_slist_maxtries);
Cvar_RegisterVariable(&net_messagetimeout);
- Cvar_RegisterVariable(&net_messagerejointimeout);
Cvar_RegisterVariable(&net_connecttimeout);
+ Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
Cvar_RegisterVariable(&cl_netlocalping);
Cvar_RegisterVariable(&cl_netpacketloss);
Cvar_RegisterVariable(&hostname);
{
if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
{
- Con_Printf("-ip option used, setting net_address to \"%s\"\n");
+ Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
Cvar_SetQuick(&net_address, com_argv[i + 1]);
}
else