]> icculus.org git repositories - divverent/darkplaces.git/blob - netconn.c
add the intoverflow lib
[divverent/darkplaces.git] / netconn.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 */
22
23 #include "quakedef.h"
24 #include "lhnet.h"
25
26 // for secure rcon authentication
27 #include "hmac.h"
28 #include "mdfour.h"
29 #include <time.h>
30
31 #define QWMASTER_PORT 27000
32 #define DPMASTER_PORT 27950
33
34 // note this defaults on for dedicated servers, off for listen servers
35 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"};
36 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
37
38 static cvar_t sv_masters [] =
39 {
40         {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
41         {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
42         {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
43         {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
44         {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
45         {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
46         {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
47         {0, NULL, NULL, NULL}
48 };
49
50 static cvar_t sv_qwmasters [] =
51 {
52         {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
53         {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
54         {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
55         {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
56         {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
57         {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
58         {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
59         {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
60         {0, "sv_qwmasterextra5", "kubus.rulez.pl:27000", "Poland master server. (admin: unknown)"},
61         {0, NULL, NULL, NULL}
62 };
63
64 static double nextheartbeattime = 0;
65
66 sizebuf_t net_message;
67 static unsigned char net_message_buf[NET_MAXMESSAGE];
68
69 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
70 cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
71 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)"};
72 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
73 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
74
75 cvar_t cl_netlocalping = {0, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
76 static cvar_t cl_netpacketloss_send = {0, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
77 static cvar_t cl_netpacketloss_receive = {0, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
78 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
79 static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
80 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
81 static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
82 static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
83 static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
84 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
85 static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
86 static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
87 static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
88 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
89 static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
90 extern cvar_t rcon_secure;
91 extern cvar_t rcon_secure_challengetimeout;
92
93 /* statistic counters */
94 static int packetsSent = 0;
95 static int packetsReSent = 0;
96 static int packetsReceived = 0;
97 static int receivedDuplicateCount = 0;
98 static int droppedDatagrams = 0;
99
100 static int unreliableMessagesSent = 0;
101 static int unreliableMessagesReceived = 0;
102 static int reliableMessagesSent = 0;
103 static int reliableMessagesReceived = 0;
104
105 double masterquerytime = -1000;
106 int masterquerycount = 0;
107 int masterreplycount = 0;
108 int serverquerycount = 0;
109 int serverreplycount = 0;
110
111 /// this is only false if there are still servers left to query
112 static qboolean serverlist_querysleep = true;
113 static qboolean serverlist_paused = false;
114 /// this is pushed a second or two ahead of realtime whenever a master server
115 /// reply is received, to avoid issuing queries while master replies are still
116 /// flooding in (which would make a mess of the ping times)
117 static double serverlist_querywaittime = 0;
118
119 static unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
120 static unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
121
122 static int cl_numsockets;
123 static lhnetsocket_t *cl_sockets[16];
124 static int sv_numsockets;
125 static lhnetsocket_t *sv_sockets[16];
126
127 netconn_t *netconn_list = NULL;
128 mempool_t *netconn_mempool = NULL;
129
130 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
131 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
132 cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
133 cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
134
135 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
136 int cl_net_extresponse_count = 0;
137 int cl_net_extresponse_last = 0;
138
139 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
140 int sv_net_extresponse_count = 0;
141 int sv_net_extresponse_last = 0;
142
143 // ServerList interface
144 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
145 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
146
147 serverlist_infofield_t serverlist_sortbyfield;
148 int serverlist_sortflags;
149
150 int serverlist_viewcount = 0;
151 serverlist_entry_t *serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
152
153 int serverlist_cachecount;
154 serverlist_entry_t serverlist_cache[SERVERLIST_TOTALSIZE];
155
156 qboolean serverlist_consoleoutput;
157
158 static int nFavorites = 0;
159 static lhnetaddress_t favorites[256];
160
161 void NetConn_UpdateFavorites(void)
162 {
163         const char *p;
164         nFavorites = 0;
165         p = net_slist_favorites.string;
166         while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
167         {
168                 if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
169                         ++nFavorites;
170         }
171 }
172
173 /// helper function to insert a value into the viewset
174 /// spare entries will be removed
175 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
176 {
177     int i;
178         if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
179                 i = serverlist_viewcount++;
180         } else {
181                 i = SERVERLIST_VIEWLISTSIZE - 1;
182         }
183
184         for( ; i > index ; i-- )
185                 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
186
187         serverlist_viewlist[index] = entry;
188 }
189
190 /// we suppose serverlist_viewcount to be valid, ie > 0
191 static void _ServerList_ViewList_Helper_Remove( int index )
192 {
193         serverlist_viewcount--;
194         for( ; index < serverlist_viewcount ; index++ )
195                 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
196 }
197
198 /// \returns true if A should be inserted before B
199 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
200 {
201         int result = 0; // > 0 if for numbers A > B and for text if A < B
202
203         if( serverlist_sortflags & SLSF_FAVORITESFIRST )
204         {
205                 if(A->info.isfavorite != B->info.isfavorite)
206                         return A->info.isfavorite;
207         }
208
209         switch( serverlist_sortbyfield ) {
210                 case SLIF_PING:
211                         result = A->info.ping - B->info.ping;
212                         break;
213                 case SLIF_MAXPLAYERS:
214                         result = A->info.maxplayers - B->info.maxplayers;
215                         break;
216                 case SLIF_NUMPLAYERS:
217                         result = A->info.numplayers - B->info.numplayers;
218                         break;
219                 case SLIF_NUMBOTS:
220                         result = A->info.numbots - B->info.numbots;
221                         break;
222                 case SLIF_NUMHUMANS:
223                         result = A->info.numhumans - B->info.numhumans;
224                         break;
225                 case SLIF_FREESLOTS:
226                         result = A->info.freeslots - B->info.freeslots;
227                         break;
228                 case SLIF_PROTOCOL:
229                         result = A->info.protocol - B->info.protocol;
230                         break;
231                 case SLIF_CNAME:
232                         result = strcmp( B->info.cname, A->info.cname );
233                         break;
234                 case SLIF_GAME:
235                         result = strcasecmp( B->info.game, A->info.game );
236                         break;
237                 case SLIF_MAP:
238                         result = strcasecmp( B->info.map, A->info.map );
239                         break;
240                 case SLIF_MOD:
241                         result = strcasecmp( B->info.mod, A->info.mod );
242                         break;
243                 case SLIF_NAME:
244                         result = strcasecmp( B->info.name, A->info.name );
245                         break;
246                 case SLIF_QCSTATUS:
247                         result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
248                         break;
249                 case SLIF_ISFAVORITE:
250                         result = !!B->info.isfavorite - !!A->info.isfavorite;
251                         break;
252                 default:
253                         Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
254                         break;
255         }
256
257         if (result != 0)
258         {
259                 if( serverlist_sortflags & SLSF_DESCENDING )
260                         return result > 0;
261                 else
262                         return result < 0;
263         }
264
265         // if the chosen sort key is identical, sort by index
266         // (makes this a stable sort, so that later replies from servers won't
267         //  shuffle the servers around when they have the same ping)
268         return A < B;
269 }
270
271 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
272 {
273         // This should actually be done with some intermediate and end-of-function return
274         switch( op ) {
275                 case SLMO_LESS:
276                         return A < B;
277                 case SLMO_LESSEQUAL:
278                         return A <= B;
279                 case SLMO_EQUAL:
280                         return A == B;
281                 case SLMO_GREATER:
282                         return A > B;
283                 case SLMO_NOTEQUAL:
284                         return A != B;
285                 case SLMO_GREATEREQUAL:
286                 case SLMO_CONTAINS:
287                 case SLMO_NOTCONTAIN:
288                 case SLMO_STARTSWITH:
289                 case SLMO_NOTSTARTSWITH:
290                         return A >= B;
291                 default:
292                         Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
293                         return false;
294         }
295 }
296
297 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
298 {
299         int i;
300         char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
301         COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
302         for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
303                 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
304         bufferA[i] = 0;
305         for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
306                 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
307         bufferB[i] = 0;
308
309         // Same here, also using an intermediate & final return would be more appropriate
310         // A info B mask
311         switch( op ) {
312                 case SLMO_CONTAINS:
313                         return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
314                 case SLMO_NOTCONTAIN:
315                         return !*bufferB || !strstr( bufferA, bufferB );
316                 case SLMO_STARTSWITH:
317                         //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
318                         return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
319                 case SLMO_NOTSTARTSWITH:
320                         return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
321                 case SLMO_LESS:
322                         return strcmp( bufferA, bufferB ) < 0;
323                 case SLMO_LESSEQUAL:
324                         return strcmp( bufferA, bufferB ) <= 0;
325                 case SLMO_EQUAL:
326                         return strcmp( bufferA, bufferB ) == 0;
327                 case SLMO_GREATER:
328                         return strcmp( bufferA, bufferB ) > 0;
329                 case SLMO_NOTEQUAL:
330                         return strcmp( bufferA, bufferB ) != 0;
331                 case SLMO_GREATEREQUAL:
332                         return strcmp( bufferA, bufferB ) >= 0;
333                 default:
334                         Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
335                         return false;
336         }
337 }
338
339 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
340 {
341         if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
342                 return false;
343         if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
344                 return false;
345         if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
346                 return false;
347         if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
348                 return false;
349         if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
350                 return false;
351         if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
352                 return false;
353         if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
354                 return false;
355         if( *mask->info.cname
356                 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
357                 return false;
358         if( *mask->info.game
359                 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
360                 return false;
361         if( *mask->info.mod
362                 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
363                 return false;
364         if( *mask->info.map
365                 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
366                 return false;
367         if( *mask->info.name
368                 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
369                 return false;
370         if( *mask->info.qcstatus
371                 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
372                 return false;
373         if( *mask->info.players
374                 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
375                 return false;
376         if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
377                 return false;
378         return true;
379 }
380
381 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
382 {
383         int start, end, mid, i;
384         lhnetaddress_t addr;
385
386         // reject incompatible servers
387         if(
388                 entry->info.gameversion != gameversion.integer
389                 &&
390                 !(
391                            gameversion_min.integer >= 0 // min/max range set by user/mod?
392                         && gameversion_max.integer >= 0
393                         && gameversion_min.integer >= entry->info.gameversion // version of server in min/max range?
394                         && gameversion_max.integer <= entry->info.gameversion
395                  )
396         )
397                 return;
398
399         // refresh the "favorite" status
400         entry->info.isfavorite = false;
401         if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
402         {
403                 for(i = 0; i < nFavorites; ++i)
404                 {
405                         if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
406                         {
407                                 entry->info.isfavorite = true;
408                                 break;
409                         }
410                 }
411         }
412
413         // FIXME: change this to be more readable (...)
414         // now check whether it passes through the masks
415         for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
416                 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
417                         return;
418
419         for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
420                 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
421                         break;
422         if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
423                 return;
424
425         if( !serverlist_viewcount ) {
426                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
427                 return;
428         }
429         // ok, insert it, we just need to find out where exactly:
430
431         // two special cases
432         // check whether to insert it as new first item
433         if( _ServerList_Entry_Compare( entry, serverlist_viewlist[0] ) ) {
434                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
435                 return;
436         } // check whether to insert it as new last item
437         else if( !_ServerList_Entry_Compare( entry, serverlist_viewlist[serverlist_viewcount - 1] ) ) {
438                 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
439                 return;
440         }
441         start = 0;
442         end = serverlist_viewcount - 1;
443         while( end > start + 1 )
444         {
445                 mid = (start + end) / 2;
446                 // test the item that lies in the middle between start and end
447                 if( _ServerList_Entry_Compare( entry, serverlist_viewlist[mid] ) )
448                         // the item has to be in the upper half
449                         end = mid;
450                 else
451                         // the item has to be in the lower half
452                         start = mid;
453         }
454         _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
455 }
456
457 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
458 {
459         int i;
460         for( i = 0; i < serverlist_viewcount; i++ )
461         {
462                 if (serverlist_viewlist[i] == entry)
463                 {
464                         _ServerList_ViewList_Helper_Remove(i);
465                         break;
466                 }
467         }
468 }
469
470 void ServerList_RebuildViewList(void)
471 {
472         int i;
473
474         serverlist_viewcount = 0;
475         for( i = 0 ; i < serverlist_cachecount ; i++ ) {
476                 serverlist_entry_t *entry = &serverlist_cache[i];
477                 // also display entries that are currently being refreshed [11/8/2007 Black]
478                 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
479                         ServerList_ViewList_Insert( entry );
480         }
481 }
482
483 void ServerList_ResetMasks(void)
484 {
485         int i;
486
487         memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
488         memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
489         // numbots needs to be compared to -1 to always succeed
490         for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
491                 serverlist_andmasks[i].info.numbots = -1;
492         for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
493                 serverlist_ormasks[i].info.numbots = -1;
494 }
495
496 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
497 {
498         int i;
499         int numplayers = 0, maxplayers = 0;
500         for (i = 0;i < serverlist_cachecount;i++)
501         {
502                 if (serverlist_cache[i].query == SQS_QUERIED)
503                 {
504                         numplayers += serverlist_cache[i].info.numhumans;
505                         maxplayers += serverlist_cache[i].info.maxplayers;
506                 }
507         }
508         *numplayerspointer = numplayers;
509         *maxplayerspointer = maxplayers;
510 }
511
512 #if 0
513 static void _ServerList_Test(void)
514 {
515         int i;
516         for( i = 0 ; i < 1024 ; i++ ) {
517                 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
518                 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
519                 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
520                 serverlist_cache[serverlist_cachecount].finished = true;
521                 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
522                 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
523                 serverlist_cachecount++;
524         }
525 }
526 #endif
527
528 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
529 {
530         masterquerytime = realtime;
531         masterquerycount = 0;
532         masterreplycount = 0;
533         if( resetcache ) {
534                 serverquerycount = 0;
535                 serverreplycount = 0;
536                 serverlist_cachecount = 0;
537                 serverlist_viewcount = 0;
538         } else {
539                 // refresh all entries
540                 int n;
541                 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
542                         serverlist_entry_t *entry = &serverlist_cache[ n ];
543                         entry->query = SQS_REFRESHING;
544                         entry->querycounter = 0;
545                 }
546         }
547         serverlist_consoleoutput = consoleoutput;
548
549         //_ServerList_Test();
550
551         NetConn_QueryMasters(querydp, queryqw);
552 }
553
554 // rest
555
556 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
557 {
558         int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
559         int i;
560         if (length == 0)
561                 return 0;
562         if (cl_netpacketloss_receive.integer)
563                 for (i = 0;i < cl_numsockets;i++)
564                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
565                                 return 0;
566         if (developer_networking.integer)
567         {
568                 char addressstring[128], addressstring2[128];
569                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
570                 if (length > 0)
571                 {
572                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
573                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
574                         Com_HexDumpToConsole((unsigned char *)data, length);
575                 }
576                 else
577                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
578         }
579         return length;
580 }
581
582 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
583 {
584         int ret;
585         int i;
586         if (cl_netpacketloss_send.integer)
587                 for (i = 0;i < cl_numsockets;i++)
588                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
589                                 return length;
590         ret = LHNET_Write(mysocket, data, length, peeraddress);
591         if (developer_networking.integer)
592         {
593                 char addressstring[128], addressstring2[128];
594                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
595                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
596                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
597                 Com_HexDumpToConsole((unsigned char *)data, length);
598         }
599         return ret;
600 }
601
602 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
603 {
604         // note this does not include the trailing NULL because we add that in the parser
605         return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
606 }
607
608 qboolean NetConn_CanSend(netconn_t *conn)
609 {
610         conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
611         conn->outgoing_netgraph[conn->outgoing_packetcounter].time            = realtime;
612         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
613         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
614         conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
615         if (realtime > conn->cleartime)
616                 return true;
617         else
618         {
619                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
620                 return false;
621         }
622 }
623
624 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
625 {
626         int totallen = 0;
627
628         // if this packet was supposedly choked, but we find ourselves sending one
629         // anyway, make sure the size counting starts at zero
630         // (this mostly happens on level changes and disconnects and such)
631         if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
632                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
633
634         if (protocol == PROTOCOL_QUAKEWORLD)
635         {
636                 int packetLen;
637                 qboolean sendreliable;
638
639                 // note that it is ok to send empty messages to the qw server,
640                 // otherwise it won't respond to us at all
641
642                 sendreliable = false;
643                 // if the remote side dropped the last reliable message, resend it
644                 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
645                         sendreliable = true;
646                 // if the reliable transmit buffer is empty, copy the current message out
647                 if (!conn->sendMessageLength && conn->message.cursize)
648                 {
649                         memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
650                         conn->sendMessageLength = conn->message.cursize;
651                         SZ_Clear(&conn->message); // clear the message buffer
652                         conn->qw.reliable_sequence ^= 1;
653                         sendreliable = true;
654                 }
655                 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
656                 StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
657                 // last received unreliable packet number, and last received reliable packet number (0 or 1)
658                 StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
659                 packetLen = 8;
660                 conn->outgoing_unreliable_sequence++;
661                 // client sends qport in every packet
662                 if (conn == cls.netcon)
663                 {
664                         *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
665                         packetLen += 2;
666                         // also update cls.qw_outgoing_sequence
667                         cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
668                 }
669                 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
670                 {
671                         Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
672                         return -1;
673                 }
674
675                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
676
677                 // add the reliable message if there is one
678                 if (sendreliable)
679                 {
680                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
681                         memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
682                         packetLen += conn->sendMessageLength;
683                         conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
684                 }
685
686                 // add the unreliable message if possible
687                 if (packetLen + data->cursize <= 1400)
688                 {
689                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
690                         memcpy(sendbuffer + packetLen, data->data, data->cursize);
691                         packetLen += data->cursize;
692                 }
693
694                 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
695
696                 packetsSent++;
697                 unreliableMessagesSent++;
698
699                 totallen += packetLen + 28;
700         }
701         else
702         {
703                 unsigned int packetLen;
704                 unsigned int dataLen;
705                 unsigned int eom;
706
707                 // if a reliable message fragment has been lost, send it again
708                 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
709                 {
710                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
711                         {
712                                 dataLen = conn->sendMessageLength;
713                                 eom = NETFLAG_EOM;
714                         }
715                         else
716                         {
717                                 dataLen = MAX_PACKETFRAGMENT;
718                                 eom = 0;
719                         }
720
721                         packetLen = NET_HEADERSIZE + dataLen;
722
723                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
724                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
725                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
726
727                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
728
729                         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
730                         {
731                                 conn->lastSendTime = realtime;
732                                 packetsReSent++;
733                         }
734
735                         totallen += packetLen + 28;
736                 }
737
738                 // if we have a new reliable message to send, do so
739                 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
740                 {
741                         if (conn->message.cursize > (int)sizeof(conn->sendMessage))
742                         {
743                                 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
744                                 conn->message.overflowed = true;
745                                 return -1;
746                         }
747
748                         if (developer_networking.integer && conn == cls.netcon)
749                         {
750                                 Con_Print("client sending reliable message to server:\n");
751                                 SZ_HexDumpToConsole(&conn->message);
752                         }
753
754                         memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
755                         conn->sendMessageLength = conn->message.cursize;
756                         SZ_Clear(&conn->message);
757
758                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
759                         {
760                                 dataLen = conn->sendMessageLength;
761                                 eom = NETFLAG_EOM;
762                         }
763                         else
764                         {
765                                 dataLen = MAX_PACKETFRAGMENT;
766                                 eom = 0;
767                         }
768
769                         packetLen = NET_HEADERSIZE + dataLen;
770
771                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
772                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
773                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
774
775                         conn->nq.sendSequence++;
776
777                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
778
779                         NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
780
781                         conn->lastSendTime = realtime;
782                         packetsSent++;
783                         reliableMessagesSent++;
784
785                         totallen += packetLen + 28;
786                 }
787
788                 // if we have an unreliable message to send, do so
789                 if (data->cursize)
790                 {
791                         packetLen = NET_HEADERSIZE + data->cursize;
792
793                         if (packetLen > (int)sizeof(sendbuffer))
794                         {
795                                 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
796                                 return -1;
797                         }
798
799                         StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
800                         StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
801                         memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
802
803                         conn->outgoing_unreliable_sequence++;
804
805                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
806
807                         NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
808
809                         packetsSent++;
810                         unreliableMessagesSent++;
811
812                         totallen += packetLen + 28;
813                 }
814         }
815
816         // delay later packets to obey rate limit
817         if (conn->cleartime < realtime - 0.1)
818                 conn->cleartime = realtime - 0.1;
819         conn->cleartime = conn->cleartime + (double)totallen / (double)rate;
820         if (conn->cleartime < realtime)
821                 conn->cleartime = realtime;
822
823         return 0;
824 }
825
826 qboolean NetConn_HaveClientPorts(void)
827 {
828         return !!cl_numsockets;
829 }
830
831 qboolean NetConn_HaveServerPorts(void)
832 {
833         return !!sv_numsockets;
834 }
835
836 void NetConn_CloseClientPorts(void)
837 {
838         for (;cl_numsockets > 0;cl_numsockets--)
839                 if (cl_sockets[cl_numsockets - 1])
840                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
841 }
842
843 void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
844 {
845         lhnetaddress_t address;
846         lhnetsocket_t *s;
847         int success;
848         char addressstring2[1024];
849         if (addressstring && addressstring[0])
850                 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
851         else
852                 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
853         if (success)
854         {
855                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
856                 {
857                         cl_sockets[cl_numsockets++] = s;
858                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
859                         if (addresstype != LHNETADDRESSTYPE_LOOP)
860                                 Con_Printf("Client opened a socket on address %s\n", addressstring2);
861                 }
862                 else
863                 {
864                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
865                         Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
866                 }
867         }
868         else
869                 Con_Printf("Client unable to parse address %s\n", addressstring);
870 }
871
872 void NetConn_OpenClientPorts(void)
873 {
874         int port;
875         NetConn_CloseClientPorts();
876         port = bound(0, cl_netport.integer, 65535);
877         if (cl_netport.integer != port)
878                 Cvar_SetValueQuick(&cl_netport, port);
879         if(port == 0)
880                 Con_Printf("Client using an automatically assigned port\n");
881         else
882                 Con_Printf("Client using port %i\n", port);
883         NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
884         NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
885 #ifdef SUPPORTIPV6
886         NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
887 #endif
888 }
889
890 void NetConn_CloseServerPorts(void)
891 {
892         for (;sv_numsockets > 0;sv_numsockets--)
893                 if (sv_sockets[sv_numsockets - 1])
894                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
895 }
896
897 qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
898 {
899         lhnetaddress_t address;
900         lhnetsocket_t *s;
901         int port;
902         char addressstring2[1024];
903         int success;
904
905         for (port = defaultport; port <= defaultport + range; port++)
906         {
907                 if (addressstring && addressstring[0])
908                         success = LHNETADDRESS_FromString(&address, addressstring, port);
909                 else
910                         success = LHNETADDRESS_FromPort(&address, addresstype, port);
911                 if (success)
912                 {
913                         if ((s = LHNET_OpenSocket_Connectionless(&address)))
914                         {
915                                 sv_sockets[sv_numsockets++] = s;
916                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
917                                 if (addresstype != LHNETADDRESSTYPE_LOOP)
918                                         Con_Printf("Server listening on address %s\n", addressstring2);
919                                 return true;
920                         }
921                         else
922                         {
923                                 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
924                                 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
925                         }
926                 }
927                 else
928                 {
929                         Con_Printf("Server unable to parse address %s\n", addressstring);
930                         // if it cant parse one address, it wont be able to parse another for sure
931                         return false;
932                 }
933         }
934         return false;
935 }
936
937 void NetConn_OpenServerPorts(int opennetports)
938 {
939         int port;
940         NetConn_CloseServerPorts();
941         NetConn_UpdateSockets();
942         port = bound(0, sv_netport.integer, 65535);
943         if (port == 0)
944                 port = 26000;
945         Con_Printf("Server using port %i\n", port);
946         if (sv_netport.integer != port)
947                 Cvar_SetValueQuick(&sv_netport, port);
948         if (cls.state != ca_dedicated)
949                 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
950         if (opennetports)
951         {
952 #ifdef SUPPORTIPV6
953                 qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
954                 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
955 #else
956                 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
957 #endif
958         }
959         if (sv_numsockets == 0)
960                 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
961 }
962
963 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
964 {
965         int i, a = LHNETADDRESS_GetAddressType(address);
966         for (i = 0;i < cl_numsockets;i++)
967                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
968                         return cl_sockets[i];
969         return NULL;
970 }
971
972 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
973 {
974         int i, a = LHNETADDRESS_GetAddressType(address);
975         for (i = 0;i < sv_numsockets;i++)
976                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
977                         return sv_sockets[i];
978         return NULL;
979 }
980
981 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
982 {
983         netconn_t *conn;
984         conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
985         conn->mysocket = mysocket;
986         conn->peeraddress = *peeraddress;
987         conn->lastMessageTime = realtime;
988         conn->message.data = conn->messagedata;
989         conn->message.maxsize = sizeof(conn->messagedata);
990         conn->message.cursize = 0;
991         // LordHavoc: (inspired by ProQuake) use a short connect timeout to
992         // reduce effectiveness of connection request floods
993         conn->timeout = realtime + net_connecttimeout.value;
994         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
995         conn->next = netconn_list;
996         netconn_list = conn;
997         return conn;
998 }
999
1000 void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress);
1001 void NetConn_Close(netconn_t *conn)
1002 {
1003         netconn_t *c;
1004         // remove connection from list
1005
1006         // allow the client to reconnect immediately
1007         NetConn_ClearConnectFlood(&(conn->peeraddress));
1008
1009         if (conn == netconn_list)
1010                 netconn_list = conn->next;
1011         else
1012         {
1013                 for (c = netconn_list;c;c = c->next)
1014                 {
1015                         if (c->next == conn)
1016                         {
1017                                 c->next = conn->next;
1018                                 break;
1019                         }
1020                 }
1021                 // not found in list, we'll avoid crashing here...
1022                 if (!c)
1023                         return;
1024         }
1025         // free connection
1026         Mem_Free(conn);
1027 }
1028
1029 static int clientport = -1;
1030 static int clientport2 = -1;
1031 static int hostport = -1;
1032 void NetConn_UpdateSockets(void)
1033 {
1034         int i, j;
1035
1036         if (cls.state != ca_dedicated)
1037         {
1038                 if (clientport2 != cl_netport.integer)
1039                 {
1040                         clientport2 = cl_netport.integer;
1041                         if (cls.state == ca_connected)
1042                                 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1043                 }
1044                 if (cls.state == ca_disconnected && clientport != clientport2)
1045                 {
1046                         clientport = clientport2;
1047                         NetConn_CloseClientPorts();
1048                 }
1049                 if (cl_numsockets == 0)
1050                         NetConn_OpenClientPorts();
1051         }
1052
1053         if (hostport != sv_netport.integer)
1054         {
1055                 hostport = sv_netport.integer;
1056                 if (sv.active)
1057                         Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1058         }
1059
1060         for (j = 0;j < MAX_RCONS;j++)
1061         {
1062                 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1063                 if(cls.rcon_commands[i][0])
1064                 {
1065                         if(realtime > cls.rcon_timeout[i])
1066                         {
1067                                 char s[128];
1068                                 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1069                                 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1070                                 cls.rcon_commands[i][0] = 0;
1071                                 --cls.rcon_trying;
1072                                 break;
1073                         }
1074                 }
1075         }
1076 }
1077
1078 static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout)
1079 {
1080         int originallength = length;
1081         if (length < 8)
1082                 return 0;
1083
1084         if (protocol == PROTOCOL_QUAKEWORLD)
1085         {
1086                 int sequence, sequence_ack;
1087                 int reliable_ack, reliable_message;
1088                 int count;
1089                 int qport;
1090
1091                 sequence = LittleLong(*((int *)(data + 0)));
1092                 sequence_ack = LittleLong(*((int *)(data + 4)));
1093                 data += 8;
1094                 length -= 8;
1095
1096                 if (conn != cls.netcon)
1097                 {
1098                         // server only
1099                         if (length < 2)
1100                                 return 0;
1101                         // TODO: use qport to identify that this client really is who they say they are?  (and elsewhere in the code to identify the connection without a port match?)
1102                         qport = LittleShort(*((int *)(data + 8)));
1103                         data += 2;
1104                         length -= 2;
1105                 }
1106
1107                 packetsReceived++;
1108                 reliable_message = (sequence >> 31) & 1;
1109                 reliable_ack = (sequence_ack >> 31) & 1;
1110                 sequence &= ~(1<<31);
1111                 sequence_ack &= ~(1<<31);
1112                 if (sequence <= conn->qw.incoming_sequence)
1113                 {
1114                         //Con_DPrint("Got a stale datagram\n");
1115                         return 0;
1116                 }
1117                 count = sequence - (conn->qw.incoming_sequence + 1);
1118                 if (count > 0)
1119                 {
1120                         droppedDatagrams += count;
1121                         //Con_DPrintf("Dropped %u datagram(s)\n", count);
1122                         while (count--)
1123                         {
1124                                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1125                                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1126                                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1127                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1128                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1129                         }
1130                 }
1131                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1132                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1133                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1134                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1135                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1136                 if (reliable_ack == conn->qw.reliable_sequence)
1137                 {
1138                         // received, now we will be able to send another reliable message
1139                         conn->sendMessageLength = 0;
1140                         reliableMessagesReceived++;
1141                 }
1142                 conn->qw.incoming_sequence = sequence;
1143                 if (conn == cls.netcon)
1144                         cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1145                 conn->qw.incoming_acknowledged = sequence_ack;
1146                 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1147                 if (reliable_message)
1148                         conn->qw.incoming_reliable_sequence ^= 1;
1149                 conn->lastMessageTime = realtime;
1150                 conn->timeout = realtime + newtimeout;
1151                 unreliableMessagesReceived++;
1152                 SZ_Clear(&net_message);
1153                 SZ_Write(&net_message, data, length);
1154                 MSG_BeginReading();
1155                 return 2;
1156         }
1157         else
1158         {
1159                 unsigned int count;
1160                 unsigned int flags;
1161                 unsigned int sequence;
1162                 int qlength;
1163
1164                 qlength = (unsigned int)BuffBigLong(data);
1165                 flags = qlength & ~NETFLAG_LENGTH_MASK;
1166                 qlength &= NETFLAG_LENGTH_MASK;
1167                 // control packets were already handled
1168                 if (!(flags & NETFLAG_CTL) && qlength == length)
1169                 {
1170                         sequence = BuffBigLong(data + 4);
1171                         packetsReceived++;
1172                         data += 8;
1173                         length -= 8;
1174                         if (flags & NETFLAG_UNRELIABLE)
1175                         {
1176                                 if (sequence >= conn->nq.unreliableReceiveSequence)
1177                                 {
1178                                         if (sequence > conn->nq.unreliableReceiveSequence)
1179                                         {
1180                                                 count = sequence - conn->nq.unreliableReceiveSequence;
1181                                                 droppedDatagrams += count;
1182                                                 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1183                                                 while (count--)
1184                                                 {
1185                                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1186                                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1187                                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1188                                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1189                                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1190                                                 }
1191                                         }
1192                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1193                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
1194                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1195                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1196                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1197                                         conn->nq.unreliableReceiveSequence = sequence + 1;
1198                                         conn->lastMessageTime = realtime;
1199                                         conn->timeout = realtime + newtimeout;
1200                                         unreliableMessagesReceived++;
1201                                         if (length > 0)
1202                                         {
1203                                                 SZ_Clear(&net_message);
1204                                                 SZ_Write(&net_message, data, length);
1205                                                 MSG_BeginReading();
1206                                                 return 2;
1207                                         }
1208                                 }
1209                                 //else
1210                                 //      Con_DPrint("Got a stale datagram\n");
1211                                 return 1;
1212                         }
1213                         else if (flags & NETFLAG_ACK)
1214                         {
1215                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1216                                 if (sequence == (conn->nq.sendSequence - 1))
1217                                 {
1218                                         if (sequence == conn->nq.ackSequence)
1219                                         {
1220                                                 conn->nq.ackSequence++;
1221                                                 if (conn->nq.ackSequence != conn->nq.sendSequence)
1222                                                         Con_DPrint("ack sequencing error\n");
1223                                                 conn->lastMessageTime = realtime;
1224                                                 conn->timeout = realtime + newtimeout;
1225                                                 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1226                                                 {
1227                                                         unsigned int packetLen;
1228                                                         unsigned int dataLen;
1229                                                         unsigned int eom;
1230
1231                                                         conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1232                                                         memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1233
1234                                                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1235                                                         {
1236                                                                 dataLen = conn->sendMessageLength;
1237                                                                 eom = NETFLAG_EOM;
1238                                                         }
1239                                                         else
1240                                                         {
1241                                                                 dataLen = MAX_PACKETFRAGMENT;
1242                                                                 eom = 0;
1243                                                         }
1244
1245                                                         packetLen = NET_HEADERSIZE + dataLen;
1246
1247                                                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
1248                                                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1249                                                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1250
1251                                                         conn->nq.sendSequence++;
1252
1253                                                         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
1254                                                         {
1255                                                                 conn->lastSendTime = realtime;
1256                                                                 packetsSent++;
1257                                                         }
1258                                                 }
1259                                                 else
1260                                                         conn->sendMessageLength = 0;
1261                                         }
1262                                         //else
1263                                         //      Con_DPrint("Duplicate ACK received\n");
1264                                 }
1265                                 //else
1266                                 //      Con_DPrint("Stale ACK received\n");
1267                                 return 1;
1268                         }
1269                         else if (flags & NETFLAG_DATA)
1270                         {
1271                                 unsigned char temppacket[8];
1272                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
1273                                 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
1274                                 StoreBigLong(temppacket, 8 | NETFLAG_ACK);
1275                                 StoreBigLong(temppacket + 4, sequence);
1276                                 NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
1277                                 if (sequence == conn->nq.receiveSequence)
1278                                 {
1279                                         conn->lastMessageTime = realtime;
1280                                         conn->timeout = realtime + newtimeout;
1281                                         conn->nq.receiveSequence++;
1282                                         if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1283                                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1284                                                 conn->receiveMessageLength += length;
1285                                         } else {
1286                                                 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1287                                                                         "Dropping the message!\n", sequence );
1288                                                 conn->receiveMessageLength = 0;
1289                                                 return 1;
1290                                         }
1291                                         if (flags & NETFLAG_EOM)
1292                                         {
1293                                                 reliableMessagesReceived++;
1294                                                 length = conn->receiveMessageLength;
1295                                                 conn->receiveMessageLength = 0;
1296                                                 if (length > 0)
1297                                                 {
1298                                                         SZ_Clear(&net_message);
1299                                                         SZ_Write(&net_message, conn->receiveMessage, length);
1300                                                         MSG_BeginReading();
1301                                                         return 2;
1302                                                 }
1303                                         }
1304                                 }
1305                                 else
1306                                         receivedDuplicateCount++;
1307                                 return 1;
1308                         }
1309                 }
1310         }
1311         return 0;
1312 }
1313
1314 void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1315 {
1316         cls.connect_trying = false;
1317         M_Update_Return_Reason("");
1318         // the connection request succeeded, stop current connection and set up a new connection
1319         CL_Disconnect();
1320         // if we're connecting to a remote server, shut down any local server
1321         if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1322                 Host_ShutdownServer ();
1323         // allocate a net connection to keep track of things
1324         cls.netcon = NetConn_Open(mysocket, peeraddress);
1325         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1326         key_dest = key_game;
1327         m_state = m_none;
1328         cls.demonum = -1;                       // not in the demo loop now
1329         cls.state = ca_connected;
1330         cls.signon = 0;                         // need all the signon messages before playing
1331         cls.protocol = initialprotocol;
1332         // reset move sequence numbering on this new connection
1333         cls.servermovesequence = 0;
1334         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1335                 Cmd_ForwardStringToServer("new");
1336         if (cls.protocol == PROTOCOL_QUAKE)
1337         {
1338                 // write a keepalive (clc_nop) as it seems to greatly improve the
1339                 // chances of connecting to a netquake server
1340                 sizebuf_t msg;
1341                 unsigned char buf[4];
1342                 memset(&msg, 0, sizeof(msg));
1343                 msg.data = buf;
1344                 msg.maxsize = sizeof(buf);
1345                 MSG_WriteChar(&msg, clc_nop);
1346                 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
1347         }
1348 }
1349
1350 int NetConn_IsLocalGame(void)
1351 {
1352         if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1353                 return true;
1354         return false;
1355 }
1356
1357 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1358 {
1359         int n;
1360         int pingtime;
1361         serverlist_entry_t *entry = NULL;
1362
1363         // search the cache for this server and update it
1364         for (n = 0;n < serverlist_cachecount;n++) {
1365                 entry = &serverlist_cache[ n ];
1366                 if (!strcmp(addressstring, entry->info.cname))
1367                         break;
1368         }
1369
1370         if (n == serverlist_cachecount)
1371         {
1372                 // LAN search doesnt require an answer from the master server so we wont
1373                 // know the ping nor will it be initialized already...
1374
1375                 // find a slot
1376                 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1377                         return -1;
1378
1379                 entry = &serverlist_cache[n];
1380
1381                 memset(entry, 0, sizeof(*entry));
1382                 // store the data the engine cares about (address and ping)
1383                 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1384                 entry->info.ping = 100000;
1385                 entry->querytime = realtime;
1386                 // if not in the slist menu we should print the server to console
1387                 if (serverlist_consoleoutput)
1388                         Con_Printf("querying %s\n", addressstring);
1389                 ++serverlist_cachecount;
1390         }
1391         // if this is the first reply from this server, count it as having replied
1392         pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1393         pingtime = bound(0, pingtime, 9999);
1394         if (entry->query == SQS_REFRESHING) {
1395                 entry->info.ping = pingtime;
1396                 entry->query = SQS_QUERIED;
1397         } else {
1398                 // convert to unsigned to catch the -1
1399                 // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
1400                 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1401                 serverreplycount++;
1402         }
1403         
1404         // other server info is updated by the caller
1405         return n;
1406 }
1407
1408 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1409 {
1410         serverlist_entry_t *entry = &serverlist_cache[n];
1411         serverlist_info_t *info = &entry->info;
1412         // update description strings for engine menu and console output
1413         dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
1414         dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1415                         (
1416                          info->gameversion != gameversion.integer
1417                          &&
1418                          !(
1419                                     gameversion_min.integer >= 0 // min/max range set by user/mod?
1420                                  && gameversion_max.integer >= 0
1421                                  && gameversion_min.integer >= info->gameversion // version of server in min/max range?
1422                                  && gameversion_max.integer <= info->gameversion
1423                           )
1424                         ) ? '1' : '4',
1425                         info->mod, info->map);
1426         if (entry->query == SQS_QUERIED)
1427         {
1428                 if(!serverlist_paused)
1429                         ServerList_ViewList_Remove(entry);
1430         }
1431         // if not in the slist menu we should print the server to console (if wanted)
1432         else if( serverlist_consoleoutput )
1433                 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1434         // and finally, update the view set
1435         if(!serverlist_paused)
1436                 ServerList_ViewList_Insert( entry );
1437         //      update the entry's state
1438         serverlist_cache[n].query = SQS_QUERIED;
1439 }
1440
1441 // returns true, if it's sensible to continue the processing
1442 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring, qboolean isfavorite ) {
1443         int n;
1444         serverlist_entry_t *entry;
1445
1446         //      ignore the rest of the message if the serverlist is full
1447         if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1448                 return false;
1449         //      also ignore     it      if      we      have already queried    it      (other master server    response)
1450         for( n =        0 ; n   < serverlist_cachecount ; n++   )
1451                 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1452                         break;
1453
1454         entry = &serverlist_cache[n];
1455
1456         if( n < serverlist_cachecount ) {
1457                 // the entry has already been queried once or 
1458                 return true;
1459         }
1460
1461         memset(entry, 0, sizeof(entry));
1462         entry->protocol =       protocol;
1463         //      store   the data        the engine cares about (address and     ping)
1464         strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1465
1466         entry->info.isfavorite = isfavorite;
1467         
1468         // no, then reset the ping right away
1469         entry->info.ping = -1;
1470         // we also want to increase the serverlist_cachecount then
1471         serverlist_cachecount++;
1472         serverquerycount++;
1473
1474         entry->query =  SQS_QUERYING;
1475
1476         return true;
1477 }
1478
1479 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
1480 {
1481         masterreplycount++;
1482         if (serverlist_consoleoutput)
1483                 Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
1484         while (length >= 7)
1485         {
1486                 char ipstring [128];
1487
1488                 // IPv4 address
1489                 if (data[0] == '\\')
1490                 {
1491                         unsigned short port = data[5] * 256 + data[6];
1492
1493                         if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1494                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1495
1496                         // move on to next address in packet
1497                         data += 7;
1498                         length -= 7;
1499                 }
1500                 // IPv6 address
1501                 else if (data[0] == '/' && isextended && length >= 19)
1502                 {
1503                         unsigned short port = data[17] * 256 + data[18];
1504
1505                         if (port != 0)
1506                         {
1507                                 const char *ifname;
1508
1509                                 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1510
1511                                 ifname = LHNETADDRESS_GetInterfaceName(senderaddress);
1512                                 if (ifname != NULL)
1513                                 {
1514                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x%%%s]:%hu",
1515                                                                 data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
1516                                                                 data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16],
1517                                                                 ifname, port);
1518                                 }
1519                                 else
1520                                 {
1521                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x]:%hu",
1522                                                                 data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
1523                                                                 data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16],
1524                                                                 port);
1525                                 }
1526                         }
1527
1528                         // move on to next address in packet
1529                         data += 19;
1530                         length -= 19;
1531                 }
1532                 else
1533                 {
1534                         Con_Print("Error while parsing the server list\n");
1535                         break;
1536                 }
1537
1538                 if (serverlist_consoleoutput && developer_networking.integer)
1539                         Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1540                 
1541                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
1542                         break;
1543                 }
1544
1545         }
1546
1547         // begin or resume serverlist queries
1548         serverlist_querysleep = false;
1549         serverlist_querywaittime = realtime + 3;
1550 }
1551
1552 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1553 {
1554         qboolean fromserver;
1555         int ret, c, control;
1556         const char *s;
1557         char *string, addressstring2[128], ipstring[32];
1558         char stringbuf[16384];
1559
1560         // quakeworld ingame packet
1561         fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1562
1563         // convert the address to a string incase we need it
1564         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1565
1566         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1567         {
1568                 // received a command string - strip off the packaging and put it
1569                 // into our string buffer with NULL termination
1570                 data += 4;
1571                 length -= 4;
1572                 length = min(length, (int)sizeof(stringbuf) - 1);
1573                 memcpy(stringbuf, data, length);
1574                 stringbuf[length] = 0;
1575                 string = stringbuf;
1576
1577                 if (developer_networking.integer)
1578                 {
1579                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1580                         Com_HexDumpToConsole(data, length);
1581                 }
1582
1583                 if (length > 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
1584                 {
1585                         int i, j;
1586                         for (j = 0;j < MAX_RCONS;j++)
1587                         {
1588                                 i = (cls.rcon_ringpos + j) % MAX_RCONS;
1589                                 if(cls.rcon_commands[i][0])
1590                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1591                                                 break;
1592                         }
1593                         if (j < MAX_RCONS)
1594                         {
1595                                 char buf[1500];
1596                                 char argbuf[1500];
1597                                 const char *e;
1598                                 int n;
1599                                 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
1600                                 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
1601
1602                                 e = strchr(rcon_password.string, ' ');
1603                                 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
1604
1605                                 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
1606                                 {
1607                                         buf[45] = ' ';
1608                                         strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
1609                                         NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
1610                                         cls.rcon_commands[i][0] = 0;
1611                                         --cls.rcon_trying;
1612
1613                                         for (i = 0;i < MAX_RCONS;i++)
1614                                                 if(cls.rcon_commands[i][0])
1615                                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1616                                                                 break;
1617                                         if(i < MAX_RCONS)
1618                                         {
1619                                                 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
1620                                                 // extend the timeout on other requests as we asked for a challenge
1621                                                 for (i = 0;i < MAX_RCONS;i++)
1622                                                         if(cls.rcon_commands[i][0])
1623                                                                 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
1624                                                                         cls.rcon_timeout[i] = realtime + rcon_secure_challengetimeout.value;
1625                                         }
1626
1627                                         return true; // we used up the challenge, so we can't use this oen for connecting now anyway
1628                                 }
1629                         }
1630                 }
1631                 if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1632                 {
1633                         // darkplaces or quake3
1634                         char protocolnames[1400];
1635                         Protocol_Names(protocolnames, sizeof(protocolnames));
1636                         Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1637                         M_Update_Return_Reason("Got challenge response");
1638                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1639                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1640                         // TODO: add userinfo stuff here instead of using NQ commands?
1641                         NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s\\challenge\\%s", protocolnames, string + 10), peeraddress);
1642                         return true;
1643                 }
1644                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1645                 {
1646                         // darkplaces or quake3
1647                         M_Update_Return_Reason("Accepted");
1648                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1649                         return true;
1650                 }
1651                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1652                 {
1653                         char rejectreason[32];
1654                         cls.connect_trying = false;
1655                         string += 7;
1656                         length = min(length - 7, (int)sizeof(rejectreason) - 1);
1657                         memcpy(rejectreason, string, length);
1658                         rejectreason[length] = 0;
1659                         M_Update_Return_Reason(rejectreason);
1660                         return true;
1661                 }
1662                 if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
1663                 {
1664                         serverlist_info_t *info;
1665                         char *p;
1666                         int n;
1667
1668                         string += 15;
1669                         // search the cache for this server and update it
1670                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1671                         if (n < 0)
1672                                 return true;
1673
1674                         info = &serverlist_cache[n].info;
1675                         info->game[0] = 0;
1676                         info->mod[0]  = 0;
1677                         info->map[0]  = 0;
1678                         info->name[0] = 0;
1679                         info->qcstatus[0] = 0;
1680                         info->players[0] = 0;
1681                         info->protocol = -1;
1682                         info->numplayers = 0;
1683                         info->numbots = -1;
1684                         info->maxplayers  = 0;
1685                         info->gameversion = 0;
1686
1687                         p = strchr(string, '\n');
1688                         if(p)
1689                         {
1690                                 *p = 0; // cut off the string there
1691                                 ++p;
1692                         }
1693                         else
1694                                 Con_Printf("statusResponse without players block?\n");
1695
1696                         if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));
1697                         if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1698                         if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1699                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));
1700                         if ((s = SearchInfostring(string, "protocol"     )) != NULL) info->protocol = atoi(s);
1701                         if ((s = SearchInfostring(string, "clients"      )) != NULL) info->numplayers = atoi(s);
1702                         if ((s = SearchInfostring(string, "bots"         )) != NULL) info->numbots = atoi(s);
1703                         if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
1704                         if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);
1705                         if ((s = SearchInfostring(string, "qcstatus"     )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1706                         if (p                                               != NULL) strlcpy(info->players, p, sizeof(info->players));
1707                         info->numhumans = info->numplayers - max(0, info->numbots);
1708                         info->freeslots = info->maxplayers - info->numplayers;
1709
1710                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1711
1712                         return true;
1713                 }
1714                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1715                 {
1716                         serverlist_info_t *info;
1717                         int n;
1718
1719                         string += 13;
1720                         // search the cache for this server and update it
1721                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1722                         if (n < 0)
1723                                 return true;
1724
1725                         info = &serverlist_cache[n].info;
1726                         info->game[0] = 0;
1727                         info->mod[0]  = 0;
1728                         info->map[0]  = 0;
1729                         info->name[0] = 0;
1730                         info->qcstatus[0] = 0;
1731                         info->players[0] = 0;
1732                         info->protocol = -1;
1733                         info->numplayers = 0;
1734                         info->numbots = -1;
1735                         info->maxplayers  = 0;
1736                         info->gameversion = 0;
1737
1738                         if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));
1739                         if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1740                         if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1741                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));
1742                         if ((s = SearchInfostring(string, "protocol"     )) != NULL) info->protocol = atoi(s);
1743                         if ((s = SearchInfostring(string, "clients"      )) != NULL) info->numplayers = atoi(s);
1744                         if ((s = SearchInfostring(string, "bots"         )) != NULL) info->numbots = atoi(s);
1745                         if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
1746                         if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);
1747                         if ((s = SearchInfostring(string, "qcstatus"     )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
1748                         info->numhumans = info->numplayers - max(0, info->numbots);
1749                         info->freeslots = info->maxplayers - info->numplayers;
1750
1751                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1752
1753                         return true;
1754                 }
1755                 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1756                 {
1757                         // Extract the IP addresses
1758                         data += 18;
1759                         length -= 18;
1760                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
1761                         return true;
1762                 }
1763                 if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1764                 {
1765                         // Extract the IP addresses
1766                         data += 21;
1767                         length -= 21;
1768                         NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
1769                         return true;
1770                 }
1771                 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1772                 {
1773                         // Extract the IP addresses
1774                         data += 2;
1775                         length -= 2;
1776                         masterreplycount++;
1777                         if (serverlist_consoleoutput)
1778                                 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
1779                         while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1780                         {
1781                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1782                                 if (serverlist_consoleoutput && developer_networking.integer)
1783                                         Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1784                                 
1785                                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring, false ) ) {
1786                                         break;
1787                                 }
1788
1789                                 // move on to next address in packet
1790                                 data += 6;
1791                                 length -= 6;
1792                         }
1793                         // begin or resume serverlist queries
1794                         serverlist_querysleep = false;
1795                         serverlist_querywaittime = realtime + 3;
1796                         return true;
1797                 }
1798                 if (!strncmp(string, "extResponse ", 12))
1799                 {
1800                         ++cl_net_extresponse_count;
1801                         if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
1802                                 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
1803                         cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
1804                         dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
1805                         return true;
1806                 }
1807                 if (!strncmp(string, "ping", 4))
1808                 {
1809                         if (developer_extra.integer)
1810                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
1811                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
1812                         return true;
1813                 }
1814                 if (!strncmp(string, "ack", 3))
1815                         return true;
1816                 // QuakeWorld compatibility
1817                 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
1818                 {
1819                         // challenge message
1820                         Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
1821                         M_Update_Return_Reason("Got QuakeWorld challenge response");
1822                         cls.qw_qport = qport.integer;
1823                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1824                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1825                         NetConn_WriteString(mysocket, va("\377\377\377\377connect %i %i %i \"%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo), peeraddress);
1826                         return true;
1827                 }
1828                 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
1829                 {
1830                         // accept message
1831                         M_Update_Return_Reason("QuakeWorld Accepted");
1832                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
1833                         return true;
1834                 }
1835                 if (length > 2 && !memcmp(string, "n\\", 2))
1836                 {
1837                         serverlist_info_t *info;
1838                         int n;
1839
1840                         // qw server status
1841                         if (serverlist_consoleoutput && developer_networking.integer >= 2)
1842                                 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
1843
1844                         string += 1;
1845                         // search the cache for this server and update it
1846                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1847                         if (n < 0)
1848                                 return true;
1849
1850                         info = &serverlist_cache[n].info;
1851                         strlcpy(info->game, "QuakeWorld", sizeof(info->game));
1852                         if ((s = SearchInfostring(string, "*gamedir"     )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
1853                         if ((s = SearchInfostring(string, "map"          )) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
1854                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
1855                         info->protocol = 0;
1856                         info->numplayers = 0; // updated below
1857                         info->numhumans = 0; // updated below
1858                         if ((s = SearchInfostring(string, "maxclients"   )) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
1859                         if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
1860
1861                         // count active players on server
1862                         // (we could gather more info, but we're just after the number)
1863                         s = strchr(string, '\n');
1864                         if (s)
1865                         {
1866                                 s++;
1867                                 while (s < string + length)
1868                                 {
1869                                         for (;s < string + length && *s != '\n';s++)
1870                                                 ;
1871                                         if (s >= string + length)
1872                                                 break;
1873                                         info->numplayers++;
1874                                         info->numhumans++;
1875                                         s++;
1876                                 }
1877                         }
1878
1879                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1880
1881                         return true;
1882                 }
1883                 if (string[0] == 'n')
1884                 {
1885                         // qw print command
1886                         Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
1887                 }
1888                 // we may not have liked the packet, but it was a command packet, so
1889                 // we're done processing this packet now
1890                 return true;
1891         }
1892         // quakeworld ingame packet
1893         if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
1894         {
1895                 ret = 0;
1896                 CL_ParseServerMessage();
1897                 return ret;
1898         }
1899         // netquake control packets, supported for compatibility only
1900         if (length >= 5 && (control = BuffBigLong(data)) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
1901         {
1902                 int n;
1903                 serverlist_info_t *info;
1904
1905                 data += 4;
1906                 length -= 4;
1907                 SZ_Clear(&net_message);
1908                 SZ_Write(&net_message, data, length);
1909                 MSG_BeginReading();
1910                 c = MSG_ReadByte();
1911                 switch (c)
1912                 {
1913                 case CCREP_ACCEPT:
1914                         if (developer_extra.integer)
1915                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
1916                         if (cls.connect_trying)
1917                         {
1918                                 lhnetaddress_t clientportaddress;
1919                                 clientportaddress = *peeraddress;
1920                                 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong());
1921                                 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1922                                 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1923                                 M_Update_Return_Reason("Accepted");
1924                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
1925                         }
1926                         break;
1927                 case CCREP_REJECT:
1928                         if (developer_extra.integer)
1929                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
1930                         cls.connect_trying = false;
1931                         M_Update_Return_Reason((char *)MSG_ReadString());
1932                         break;
1933                 case CCREP_SERVER_INFO:
1934                         if (developer_extra.integer)
1935                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
1936                         // LordHavoc: because the quake server may report weird addresses
1937                         // we just ignore it and keep the real address
1938                         MSG_ReadString();
1939                         // search the cache for this server and update it
1940                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1941                         if (n < 0)
1942                                 break;
1943
1944                         info = &serverlist_cache[n].info;
1945                         strlcpy(info->game, "Quake", sizeof(info->game));
1946                         strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
1947                         strlcpy(info->name, MSG_ReadString(), sizeof(info->name));
1948                         strlcpy(info->map , MSG_ReadString(), sizeof(info->map));
1949                         info->numplayers = MSG_ReadByte();
1950                         info->maxplayers = MSG_ReadByte();
1951                         info->protocol = MSG_ReadByte();
1952
1953                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1954
1955                         break;
1956                 case CCREP_RCON: // RocketGuy: ProQuake rcon support
1957                         if (developer_extra.integer)
1958                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
1959
1960                         Con_Printf("%s\n", MSG_ReadString());
1961                         break;
1962                 case CCREP_PLAYER_INFO:
1963                         // we got a CCREP_PLAYER_INFO??
1964                         //if (developer_extra.integer)
1965                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
1966                         break;
1967                 case CCREP_RULE_INFO:
1968                         // we got a CCREP_RULE_INFO??
1969                         //if (developer_extra.integer)
1970                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
1971                         break;
1972                 default:
1973                         break;
1974                 }
1975                 SZ_Clear(&net_message);
1976                 // we may not have liked the packet, but it was a valid control
1977                 // packet, so we're done processing this packet now
1978                 return true;
1979         }
1980         ret = 0;
1981         if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
1982                 CL_ParseServerMessage();
1983         return ret;
1984 }
1985
1986 void NetConn_QueryQueueFrame(void)
1987 {
1988         int index;
1989         int queries;
1990         int maxqueries;
1991         double timeouttime;
1992         static double querycounter = 0;
1993
1994         if(!net_slist_pause.integer && serverlist_paused)
1995                 ServerList_RebuildViewList();
1996         serverlist_paused = net_slist_pause.integer != 0;
1997
1998         if (serverlist_querysleep)
1999                 return;
2000
2001         // apply a cool down time after master server replies,
2002         // to avoid messing up the ping times on the servers
2003         if (serverlist_querywaittime > realtime)
2004                 return;
2005
2006         // each time querycounter reaches 1.0 issue a query
2007         querycounter += cl.realframetime * net_slist_queriespersecond.value;
2008         maxqueries = (int)querycounter;
2009         maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
2010         querycounter -= maxqueries;
2011
2012         if( maxqueries == 0 ) {
2013                 return;
2014         }
2015
2016         //      scan serverlist and issue queries as needed
2017         serverlist_querysleep = true;
2018
2019         timeouttime     = realtime - net_slist_timeout.value;
2020         for( index = 0, queries = 0 ;   index   < serverlist_cachecount &&      queries < maxqueries    ; index++ )
2021         {
2022                 serverlist_entry_t *entry = &serverlist_cache[ index ];
2023                 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
2024                 {
2025                         continue;
2026                 }
2027
2028                 serverlist_querysleep   = false;
2029                 if( entry->querycounter !=      0 && entry->querytime > timeouttime     )
2030                 {
2031                         continue;
2032                 }
2033
2034                 if( entry->querycounter !=      (unsigned) net_slist_maxtries.integer )
2035                 {
2036                         lhnetaddress_t  address;
2037                         int socket;
2038
2039                         LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2040                         if      (entry->protocol == PROTOCOL_QUAKEWORLD)
2041                         {
2042                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2043                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2044                         }
2045                         else
2046                         {
2047                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
2048                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getstatus", &address);
2049                         }
2050
2051                         //      update the entry fields
2052                         entry->querytime = realtime;
2053                         entry->querycounter++;
2054
2055                         // if not in the slist menu we should print the server to console
2056                         if (serverlist_consoleoutput)
2057                                 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2058
2059                         queries++;
2060                 }
2061                 else
2062                 {
2063                         // have we tried to refresh this server?
2064                         if( entry->query == SQS_REFRESHING ) {
2065                                 // yes, so update the reply count (since its not responding anymore)
2066                                 serverreplycount--;
2067                                 if(!serverlist_paused)
2068                                         ServerList_ViewList_Remove(entry);
2069                         }
2070                         entry->query = SQS_TIMEDOUT;
2071                 }
2072         }
2073 }
2074
2075 void NetConn_ClientFrame(void)
2076 {
2077         int i, length;
2078         lhnetaddress_t peeraddress;
2079         NetConn_UpdateSockets();
2080         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
2081         {
2082                 if (cls.connect_remainingtries == 0)
2083                         M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
2084                 cls.connect_nextsendtime = realtime + 1;
2085                 cls.connect_remainingtries--;
2086                 if (cls.connect_remainingtries <= -10)
2087                 {
2088                         cls.connect_trying = false;
2089                         M_Update_Return_Reason("Connect: Failed");
2090                         return;
2091                 }
2092                 // try challenge first (newer DP server or QW)
2093                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2094                 // then try netquake as a fallback (old server, or netquake)
2095                 SZ_Clear(&net_message);
2096                 // save space for the header, filled in later
2097                 MSG_WriteLong(&net_message, 0);
2098                 MSG_WriteByte(&net_message, CCREQ_CONNECT);
2099                 MSG_WriteString(&net_message, "QUAKE");
2100                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
2101                 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2102                 NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
2103                 SZ_Clear(&net_message);
2104         }
2105         for (i = 0;i < cl_numsockets;i++)
2106                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2107                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2108         NetConn_QueryQueueFrame();
2109         if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
2110         {
2111                 Con_Print("Connection timed out\n");
2112                 CL_Disconnect();
2113                 Host_ShutdownServer ();
2114         }
2115 }
2116
2117 #define MAX_CHALLENGES 128
2118 struct challenge_s
2119 {
2120         lhnetaddress_t address;
2121         double time;
2122         char string[12];
2123 }
2124 challenge[MAX_CHALLENGES];
2125
2126 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2127 {
2128         int i;
2129         char c;
2130         for (i = 0;i < bufferlength - 1;i++)
2131         {
2132                 do
2133                 {
2134                         c = rand () % (127 - 33) + 33;
2135                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2136                 buffer[i] = c;
2137         }
2138         buffer[i] = 0;
2139 }
2140
2141 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2142 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
2143 {
2144         char qcstatus[256];
2145         unsigned int nb_clients = 0, nb_bots = 0, i;
2146         int length;
2147         char teambuf[3];
2148
2149         SV_VM_Begin();
2150
2151         // How many clients are there?
2152         for (i = 0;i < (unsigned int)svs.maxclients;i++)
2153         {
2154                 if (svs.clients[i].active)
2155                 {
2156                         nb_clients++;
2157                         if (!svs.clients[i].netconnection)
2158                                 nb_bots++;
2159                 }
2160         }
2161
2162         *qcstatus = 0;
2163         if(prog->globaloffsets.worldstatus >= 0)
2164         {
2165                 const char *str = PRVM_G_STRING(prog->globaloffsets.worldstatus);
2166                 if(str && *str)
2167                 {
2168                         char *p;
2169                         const char *q;
2170                         p = qcstatus;
2171                         for(q = str; *q; ++q)
2172                                 if(*q != '\\' && *q != '\n')
2173                                         *p++ = *q;
2174                         *p = 0;
2175                 }
2176         }
2177
2178         /// \TODO: we should add more information for the full status string
2179         length = dpsnprintf(out_msg, out_size,
2180                                                 "\377\377\377\377%s\x0A"
2181                                                 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2182                                                 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2183                                                 "%s%s"
2184                                                 "%s%s"
2185                                                 "%s",
2186                                                 fullstatus ? "statusResponse" : "infoResponse",
2187                                                 gamename, com_modname, gameversion.integer, svs.maxclients,
2188                                                 nb_clients, nb_bots, sv.name, hostname.string, NET_PROTOCOL_VERSION,
2189                                                 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2190                                                 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2191                                                 fullstatus ? "\n" : "");
2192
2193         // Make sure it fits in the buffer
2194         if (length < 0)
2195                 goto bad;
2196
2197         if (fullstatus)
2198         {
2199                 char *ptr;
2200                 int left;
2201                 int savelength;
2202
2203                 savelength = length;
2204
2205                 ptr = out_msg + length;
2206                 left = (int)out_size - length;
2207
2208                 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2209                 {
2210                         client_t *cl = &svs.clients[i];
2211                         if (cl->active)
2212                         {
2213                                 int nameind, cleanind, pingvalue;
2214                                 char curchar;
2215                                 char cleanname [sizeof(cl->name)];
2216
2217                                 // Remove all characters '"' and '\' in the player name
2218                                 nameind = 0;
2219                                 cleanind = 0;
2220                                 do
2221                                 {
2222                                         curchar = cl->name[nameind++];
2223                                         if (curchar != '"' && curchar != '\\')
2224                                         {
2225                                                 cleanname[cleanind++] = curchar;
2226                                                 if (cleanind == sizeof(cleanname) - 1)
2227                                                         break;
2228                                         }
2229                                 } while (curchar != '\0');
2230                                 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2231
2232                                 pingvalue = (int)(cl->ping * 1000.0f);
2233                                 if(cl->netconnection)
2234                                         pingvalue = bound(1, pingvalue, 9999);
2235                                 else
2236                                         pingvalue = 0;
2237
2238                                 *qcstatus = 0;
2239                                 if(prog->fieldoffsets.clientstatus >= 0)
2240                                 {
2241                                         const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
2242                                         if(str && *str)
2243                                         {
2244                                                 char *p;
2245                                                 const char *q;
2246                                                 p = qcstatus;
2247                                                 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2248                                                         if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2249                                                                 *p++ = *q;
2250                                                 *p = 0;
2251                                         }
2252                                 }
2253
2254                                 if ((gamemode == GAME_NEXUIZ) && (teamplay.integer > 0))
2255                                 {
2256                                         if(cl->frags == -666) // spectator
2257                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2258                                         else if(cl->colors == 0x44) // red team
2259                                                 strlcpy(teambuf, " 1", sizeof(teambuf));
2260                                         else if(cl->colors == 0xDD) // blue team
2261                                                 strlcpy(teambuf, " 2", sizeof(teambuf));
2262                                         else if(cl->colors == 0xCC) // yellow team
2263                                                 strlcpy(teambuf, " 3", sizeof(teambuf));
2264                                         else if(cl->colors == 0x99) // pink team
2265                                                 strlcpy(teambuf, " 4", sizeof(teambuf));
2266                                         else
2267                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2268                                 }
2269                                 else
2270                                         *teambuf = 0;
2271
2272                                 // note: team number is inserted according to SoF2 protocol
2273                                 if(*qcstatus)
2274                                         length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2275                                                                                 qcstatus,
2276                                                                                 pingvalue,
2277                                                                                 teambuf,
2278                                                                                 cleanname);
2279                                 else
2280                                         length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2281                                                                                 cl->frags,
2282                                                                                 pingvalue,
2283                                                                                 teambuf,
2284                                                                                 cleanname);
2285
2286                                 if(length < 0)
2287                                 {
2288                                         // out of space?
2289                                         // turn it into an infoResponse!
2290                                         out_msg[savelength] = 0;
2291                                         memcpy(out_msg + 4, "infoResponse\x0A", 13);
2292                                         memmove(out_msg + 17, out_msg + 19, savelength - 19);
2293                                         break;
2294                                 }
2295                                 left -= length;
2296                                 ptr += length;
2297                         }
2298                 }
2299         }
2300
2301         SV_VM_End();
2302         return true;
2303
2304 bad:
2305         SV_VM_End();
2306         return false;
2307 }
2308
2309 static qboolean NetConn_PreventConnectFlood(lhnetaddress_t *peeraddress)
2310 {
2311         int floodslotnum, bestfloodslotnum;
2312         double bestfloodtime;
2313         lhnetaddress_t noportpeeraddress;
2314         // see if this is a connect flood
2315         noportpeeraddress = *peeraddress;
2316         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2317         bestfloodslotnum = 0;
2318         bestfloodtime = sv.connectfloodaddresses[bestfloodslotnum].lasttime;
2319         for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
2320         {
2321                 if (bestfloodtime >= sv.connectfloodaddresses[floodslotnum].lasttime)
2322                 {
2323                         bestfloodtime = sv.connectfloodaddresses[floodslotnum].lasttime;
2324                         bestfloodslotnum = floodslotnum;
2325                 }
2326                 if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
2327                 {
2328                         // this address matches an ongoing flood address
2329                         if (realtime < sv.connectfloodaddresses[floodslotnum].lasttime + net_connectfloodblockingtimeout.value)
2330                         {
2331                                 // renew the ban on this address so it does not expire
2332                                 // until the flood has subsided
2333                                 sv.connectfloodaddresses[floodslotnum].lasttime = realtime;
2334                                 //Con_Printf("Flood detected!\n");
2335                                 return true;
2336                         }
2337                         // the flood appears to have subsided, so allow this
2338                         bestfloodslotnum = floodslotnum; // reuse the same slot
2339                         break;
2340                 }
2341         }
2342         // begin a new timeout on this address
2343         sv.connectfloodaddresses[bestfloodslotnum].address = noportpeeraddress;
2344         sv.connectfloodaddresses[bestfloodslotnum].lasttime = realtime;
2345         //Con_Printf("Flood detection initiated!\n");
2346         return false;
2347 }
2348
2349 void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
2350 {
2351         int floodslotnum;
2352         lhnetaddress_t noportpeeraddress;
2353         // see if this is a connect flood
2354         noportpeeraddress = *peeraddress;
2355         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2356         for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
2357         {
2358                 if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
2359                 {
2360                         // this address matches an ongoing flood address
2361                         // remove the ban
2362                         sv.connectfloodaddresses[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2363                         sv.connectfloodaddresses[floodslotnum].lasttime = 0;
2364                         //Con_Printf("Flood cleared!\n");
2365                 }
2366         }
2367 }
2368
2369 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2370
2371 qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2372 {
2373         char mdfourbuf[16];
2374         long t1, t2;
2375
2376         t1 = (long) time(NULL);
2377         t2 = strtol(s, NULL, 0);
2378         if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2379                 return false;
2380
2381         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2382                 return false;
2383
2384         return !memcmp(mdfourbuf, hash, 16);
2385 }
2386
2387 qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2388 {
2389         char mdfourbuf[16];
2390         int i;
2391
2392         if(slen < (int)(sizeof(challenge[0].string)) - 1)
2393                 return false;
2394
2395         // validate the challenge
2396         for (i = 0;i < MAX_CHALLENGES;i++)
2397                 if(challenge[i].time > 0)
2398                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
2399                                 break;
2400         // if the challenge is not recognized, drop the packet
2401         if (i == MAX_CHALLENGES)
2402                 return false;
2403
2404         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
2405                 return false;
2406
2407         if(memcmp(mdfourbuf, hash, 16))
2408                 return false;
2409
2410         // unmark challenge to prevent replay attacks
2411         challenge[i].time = 0;
2412
2413         return true;
2414 }
2415
2416 qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2417 {
2418         return !strcmp(password, hash);
2419 }
2420
2421 /// returns a string describing the user level, or NULL for auth failure
2422 const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
2423 {
2424         const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2425         char buf[MAX_INPUTLINE];
2426         qboolean hasquotes;
2427         qboolean restricted = false;
2428         qboolean have_usernames = false;
2429
2430         userpass_start = rcon_password.string;
2431         while((userpass_end = strchr(userpass_start, ' ')))
2432         {
2433                 have_usernames = true;
2434                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2435                 if(buf[0])
2436                         if(comparator(peeraddress, buf, password, cs, cslen))
2437                                 goto allow;
2438                 userpass_start = userpass_end + 1;
2439         }
2440         if(userpass_start[0])
2441         {
2442                 userpass_end = userpass_start + strlen(userpass_start);
2443                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2444                         goto allow;
2445         }
2446
2447         restricted = true;
2448         have_usernames = false;
2449         userpass_start = rcon_restricted_password.string;
2450         while((userpass_end = strchr(userpass_start, ' ')))
2451         {
2452                 have_usernames = true;
2453                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2454                 if(buf[0])
2455                         if(comparator(peeraddress, buf, password, cs, cslen))
2456                                 goto check;
2457                 userpass_start = userpass_end + 1;
2458         }
2459         if(userpass_start[0])
2460         {
2461                 userpass_end = userpass_start + strlen(userpass_start);
2462                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
2463                         goto check;
2464         }
2465         
2466         return NULL; // DENIED
2467
2468 check:
2469         for(text = s; text != endpos; ++text)
2470                 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2471                         return NULL; // block possible exploits against the parser/alias expansion
2472
2473         while(s != endpos)
2474         {
2475                 size_t l = strlen(s);
2476                 if(l)
2477                 {
2478                         hasquotes = (strchr(s, '"') != NULL);
2479                         // sorry, we can't allow these substrings in wildcard expressions,
2480                         // as they can mess with the argument counts
2481                         text = rcon_restricted_commands.string;
2482                         while(COM_ParseToken_Console(&text))
2483                         {
2484                                 // com_token now contains a pattern to check for...
2485                                 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2486                                 {
2487                                         if(!hasquotes)
2488                                                 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2489                                                         goto match;
2490                                 }
2491                                 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2492                                 {
2493                                         if(!strcmp(com_token, s))
2494                                                 goto match;
2495                                 }
2496                                 else // single-arg expression? must match the beginning of the command
2497                                 {
2498                                         if(!strcmp(com_token, s))
2499                                                 goto match;
2500                                         if(!memcmp(va("%s ", com_token), s, strlen(com_token) + 1))
2501                                                 goto match;
2502                                 }
2503                         }
2504                         // if we got here, nothing matched!
2505                         return NULL;
2506                 }
2507 match:
2508                 s += l + 1;
2509         }
2510
2511 allow:
2512         userpass_startpass = strchr(userpass_start, ':');
2513         if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2514                 return va("%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2515         else
2516                 return va("%srcon", restricted ? "restricted " : "");
2517
2518         return "restricted rcon";
2519 }
2520
2521 void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos)
2522 {
2523         if(userlevel)
2524         {
2525                 // looks like a legitimate rcon command with the correct password
2526                 const char *s_ptr = s;
2527                 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2528                 while(s_ptr != endpos)
2529                 {
2530                         size_t l = strlen(s_ptr);
2531                         if(l)
2532                                 Con_Printf(" %s;", s_ptr);
2533                         s_ptr += l + 1;
2534                 }
2535                 Con_Printf("\n");
2536
2537                 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2538                         Con_Rcon_Redirect_Init(mysocket, peeraddress);
2539                 while(s != endpos)
2540                 {
2541                         size_t l = strlen(s);
2542                         if(l)
2543                         {
2544                                 client_t *host_client_save = host_client;
2545                                 Cmd_ExecuteString(s, src_command);
2546                                 host_client = host_client_save;
2547                                 // in case it is a command that changes host_client (like restart)
2548                         }
2549                         s += l + 1;
2550                 }
2551                 Con_Rcon_Redirect_End();
2552         }
2553         else
2554         {
2555                 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2556         }
2557 }
2558
2559 extern void SV_SendServerinfo (client_t *client);
2560 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2561 {
2562         int i, ret, clientnum, best;
2563         double besttime;
2564         client_t *client;
2565         char *s, *string, response[1400], addressstring2[128], stringbuf[16384];
2566         qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2567
2568         if (!sv.active)
2569                 return false;
2570
2571         // convert the address to a string incase we need it
2572         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2573
2574         // see if we can identify the sender as a local player
2575         // (this is necessary for rcon to send a reliable reply if the client is
2576         //  actually on the server, not sending remotely)
2577         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2578                 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2579                         break;
2580         if (i == svs.maxclients)
2581                 host_client = NULL;
2582
2583         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2584         {
2585                 // received a command string - strip off the packaging and put it
2586                 // into our string buffer with NULL termination
2587                 data += 4;
2588                 length -= 4;
2589                 length = min(length, (int)sizeof(stringbuf) - 1);
2590                 memcpy(stringbuf, data, length);
2591                 stringbuf[length] = 0;
2592                 string = stringbuf;
2593
2594                 if (developer_extra.integer)
2595                 {
2596                         Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2597                         Com_HexDumpToConsole(data, length);
2598                 }
2599
2600                 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -2))
2601                 {
2602                         for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2603                         {
2604                                 if(challenge[i].time > 0)
2605                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2606                                                 break;
2607                                 if (besttime > challenge[i].time)
2608                                         besttime = challenge[best = i].time;
2609                         }
2610                         // if we did not find an exact match, choose the oldest and
2611                         // update address and string
2612                         if (i == MAX_CHALLENGES)
2613                         {
2614                                 i = best;
2615                                 challenge[i].address = *peeraddress;
2616                                 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2617                         }
2618                         challenge[i].time = realtime;
2619                         // send the challenge
2620                         NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
2621                         return true;
2622                 }
2623                 if (length > 8 && !memcmp(string, "connect\\", 8) && (islocal || sv_public.integer > -2))
2624                 {
2625                         string += 7;
2626                         length -= 7;
2627
2628                         if (!(s = SearchInfostring(string, "challenge")))
2629                                 return true;
2630                         // validate the challenge
2631                         for (i = 0;i < MAX_CHALLENGES;i++)
2632                                 if(challenge[i].time > 0)
2633                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2634                                                 break;
2635                         // if the challenge is not recognized, drop the packet
2636                         if (i == MAX_CHALLENGES)
2637                                 return true;
2638
2639                         // check engine protocol
2640                         if(!(s = SearchInfostring(string, "protocol")) || strcmp(s, "darkplaces 3"))
2641                         {
2642                                 if (developer_extra.integer)
2643                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
2644                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
2645                                 return true;
2646                         }
2647
2648                         // see if this is a duplicate connection request or a disconnected
2649                         // client who is rejoining to the same client slot
2650                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2651                         {
2652                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2653                                 {
2654                                         // this is a known client...
2655                                         if (client->spawned)
2656                                         {
2657                                                 // client crashed and is coming back,
2658                                                 // keep their stuff intact
2659                                                 if (developer_extra.integer)
2660                                                         Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
2661                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2662                                                 SV_VM_Begin();
2663                                                 SV_SendServerinfo(client);
2664                                                 SV_VM_End();
2665                                         }
2666                                         else
2667                                         {
2668                                                 // client is still trying to connect,
2669                                                 // so we send a duplicate reply
2670                                                 if (developer_extra.integer)
2671                                                         Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
2672                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2673                                         }
2674                                         return true;
2675                                 }
2676                         }
2677
2678                         if (NetConn_PreventConnectFlood(peeraddress))
2679                                 return true;
2680
2681                         // find an empty client slot for this new client
2682                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2683                         {
2684                                 netconn_t *conn;
2685                                 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
2686                                 {
2687                                         // allocated connection
2688                                         if (developer_extra.integer)
2689                                                 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
2690                                         NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2691                                         // now set up the client
2692                                         SV_VM_Begin();
2693                                         SV_ConnectClient(clientnum, conn);
2694                                         SV_VM_End();
2695                                         NetConn_Heartbeat(1);
2696                                         return true;
2697                                 }
2698                         }
2699
2700                         // no empty slots found - server is full
2701                         if (developer_extra.integer)
2702                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
2703                         NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
2704
2705                         return true;
2706                 }
2707                 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
2708                 {
2709                         const char *challenge = NULL;
2710
2711                         // If there was a challenge in the getinfo message
2712                         if (length > 8 && string[7] == ' ')
2713                                 challenge = string + 8;
2714
2715                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
2716                         {
2717                                 if (developer_extra.integer)
2718                                         Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
2719                                 NetConn_WriteString(mysocket, response, peeraddress);
2720                         }
2721                         return true;
2722                 }
2723                 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
2724                 {
2725                         const char *challenge = NULL;
2726
2727                         // If there was a challenge in the getinfo message
2728                         if (length > 10 && string[9] == ' ')
2729                                 challenge = string + 10;
2730
2731                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
2732                         {
2733                                 if (developer_extra.integer)
2734                                         Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
2735                                 NetConn_WriteString(mysocket, response, peeraddress);
2736                         }
2737                         return true;
2738                 }
2739                 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
2740                 {
2741                         char *password = string + 20;
2742                         char *timeval = string + 37;
2743                         char *s = strchr(timeval, ' ');
2744                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
2745                         const char *userlevel;
2746
2747                         if(rcon_secure.integer > 1)
2748                                 return true;
2749
2750                         if(!s)
2751                                 return true; // invalid packet
2752                         ++s;
2753
2754                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
2755                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
2756                         return true;
2757                 }
2758                 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
2759                 {
2760                         char *password = string + 25;
2761                         char *challenge = string + 42;
2762                         char *s = strchr(challenge, ' ');
2763                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
2764                         const char *userlevel;
2765                         if(!s)
2766                                 return true; // invalid packet
2767                         ++s;
2768
2769                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
2770                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
2771                         return true;
2772                 }
2773                 if (length >= 5 && !memcmp(string, "rcon ", 5))
2774                 {
2775                         int i;
2776                         char *s = string + 5;
2777                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
2778                         char password[64];
2779
2780                         if(rcon_secure.integer > 0)
2781                                 return true;
2782
2783                         for (i = 0;!ISWHITESPACE(*s);s++)
2784                                 if (i < (int)sizeof(password) - 1)
2785                                         password[i++] = *s;
2786                         if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
2787                                 ++s;
2788                         password[i] = 0;
2789                         if (!ISWHITESPACE(password[0]))
2790                         {
2791                                 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
2792                                 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
2793                         }
2794                         return true;
2795                 }
2796                 if (!strncmp(string, "extResponse ", 12))
2797                 {
2798                         ++sv_net_extresponse_count;
2799                         if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
2800                                 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
2801                         sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2802                         dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
2803                         return true;
2804                 }
2805                 if (!strncmp(string, "ping", 4))
2806                 {
2807                         if (developer_extra.integer)
2808                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2809                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2810                         return true;
2811                 }
2812                 if (!strncmp(string, "ack", 3))
2813                         return true;
2814                 // we may not have liked the packet, but it was a command packet, so
2815                 // we're done processing this packet now
2816                 return true;
2817         }
2818         // netquake control packets, supported for compatibility only, and only
2819         // when running game protocols that are normally served via this connection
2820         // protocol
2821         // (this protects more modern protocols against being used for
2822         //  Quake packet flood Denial Of Service attacks)
2823         if (length >= 5 && (i = BuffBigLong(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_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3))
2824         {
2825                 int c;
2826                 int protocolnumber;
2827                 const char *protocolname;
2828                 data += 4;
2829                 length -= 4;
2830                 SZ_Clear(&net_message);
2831                 SZ_Write(&net_message, data, length);
2832                 MSG_BeginReading();
2833                 c = MSG_ReadByte();
2834                 switch (c)
2835                 {
2836                 case CCREQ_CONNECT:
2837                         if (developer_extra.integer)
2838                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
2839                         if(!islocal && sv_public.integer <= -2)
2840                                 break;
2841
2842                         protocolname = MSG_ReadString();
2843                         protocolnumber = MSG_ReadByte();
2844                         if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
2845                         {
2846                                 if (developer_extra.integer)
2847                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
2848                                 SZ_Clear(&net_message);
2849                                 // save space for the header, filled in later
2850                                 MSG_WriteLong(&net_message, 0);
2851                                 MSG_WriteByte(&net_message, CCREP_REJECT);
2852                                 MSG_WriteString(&net_message, "Incompatible version.\n");
2853                                 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2854                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2855                                 SZ_Clear(&net_message);
2856                                 break;
2857                         }
2858
2859                         // see if this connect request comes from a known client
2860                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2861                         {
2862                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2863                                 {
2864                                         // this is either a duplicate connection request
2865                                         // or coming back from a timeout
2866                                         // (if so, keep their stuff intact)
2867
2868                                         // send a reply
2869                                         if (developer_extra.integer)
2870                                                 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
2871                                         SZ_Clear(&net_message);
2872                                         // save space for the header, filled in later
2873                                         MSG_WriteLong(&net_message, 0);
2874                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
2875                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
2876                                         StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2877                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2878                                         SZ_Clear(&net_message);
2879
2880                                         // if client is already spawned, re-send the
2881                                         // serverinfo message as they'll need it to play
2882                                         if (client->spawned)
2883                                         {
2884                                                 SV_VM_Begin();
2885                                                 SV_SendServerinfo(client);
2886                                                 SV_VM_End();
2887                                         }
2888                                         return true;
2889                                 }
2890                         }
2891
2892                         // this is a new client, check for connection flood
2893                         if (NetConn_PreventConnectFlood(peeraddress))
2894                                 break;
2895
2896                         // find a slot for the new client
2897                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2898                         {
2899                                 netconn_t *conn;
2900                                 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
2901                                 {
2902                                         // connect to the client
2903                                         // everything is allocated, just fill in the details
2904                                         strlcpy (conn->address, addressstring2, sizeof (conn->address));
2905                                         if (developer_extra.integer)
2906                                                 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
2907                                         // send back the info about the server connection
2908                                         SZ_Clear(&net_message);
2909                                         // save space for the header, filled in later
2910                                         MSG_WriteLong(&net_message, 0);
2911                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
2912                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
2913                                         StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2914                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2915                                         SZ_Clear(&net_message);
2916                                         // now set up the client struct
2917                                         SV_VM_Begin();
2918                                         SV_ConnectClient(clientnum, conn);
2919                                         SV_VM_End();
2920                                         NetConn_Heartbeat(1);
2921                                         return true;
2922                                 }
2923                         }
2924
2925                         if (developer_extra.integer)
2926                                 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
2927                         // no room; try to let player know
2928                         SZ_Clear(&net_message);
2929                         // save space for the header, filled in later
2930                         MSG_WriteLong(&net_message, 0);
2931                         MSG_WriteByte(&net_message, CCREP_REJECT);
2932                         MSG_WriteString(&net_message, "Server is full.\n");
2933                         StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2934                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2935                         SZ_Clear(&net_message);
2936                         break;
2937                 case CCREQ_SERVER_INFO:
2938                         if (developer_extra.integer)
2939                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
2940                         if(!islocal && sv_public.integer <= -1)
2941                                 break;
2942                         if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
2943                         {
2944                                 int numclients;
2945                                 char myaddressstring[128];
2946                                 if (developer_extra.integer)
2947                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
2948                                 SZ_Clear(&net_message);
2949                                 // save space for the header, filled in later
2950                                 MSG_WriteLong(&net_message, 0);
2951                                 MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
2952                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
2953                                 MSG_WriteString(&net_message, myaddressstring);
2954                                 MSG_WriteString(&net_message, hostname.string);
2955                                 MSG_WriteString(&net_message, sv.name);
2956                                 // How many clients are there?
2957                                 for (i = 0, numclients = 0;i < svs.maxclients;i++)
2958                                         if (svs.clients[i].active)
2959                                                 numclients++;
2960                                 MSG_WriteByte(&net_message, numclients);
2961                                 MSG_WriteByte(&net_message, svs.maxclients);
2962                                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
2963                                 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2964                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2965                                 SZ_Clear(&net_message);
2966                         }
2967                         break;
2968                 case CCREQ_PLAYER_INFO:
2969                         if (developer_extra.integer)
2970                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
2971                         if(!islocal && sv_public.integer <= -1)
2972                                 break;
2973                         if (sv.active)
2974                         {
2975                                 int playerNumber, activeNumber, clientNumber;
2976                                 client_t *client;
2977
2978                                 playerNumber = MSG_ReadByte();
2979                                 activeNumber = -1;
2980                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
2981                                         if (client->active && ++activeNumber == playerNumber)
2982                                                 break;
2983                                 if (clientNumber != svs.maxclients)
2984                                 {
2985                                         SZ_Clear(&net_message);
2986                                         // save space for the header, filled in later
2987                                         MSG_WriteLong(&net_message, 0);
2988                                         MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
2989                                         MSG_WriteByte(&net_message, playerNumber);
2990                                         MSG_WriteString(&net_message, client->name);
2991                                         MSG_WriteLong(&net_message, client->colors);
2992                                         MSG_WriteLong(&net_message, client->frags);
2993                                         MSG_WriteLong(&net_message, (int)(realtime - client->connecttime));
2994                                         MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
2995                                         StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2996                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2997                                         SZ_Clear(&net_message);
2998                                 }
2999                         }
3000                         break;
3001                 case CCREQ_RULE_INFO:
3002                         if (developer_extra.integer)
3003                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3004                         if(!islocal && sv_public.integer <= -1)
3005                                 break;
3006                         if (sv.active)
3007                         {
3008                                 char *prevCvarName;
3009                                 cvar_t *var;
3010
3011                                 // find the search start location
3012                                 prevCvarName = MSG_ReadString();
3013                                 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3014
3015                                 // send the response
3016                                 SZ_Clear(&net_message);
3017                                 // save space for the header, filled in later
3018                                 MSG_WriteLong(&net_message, 0);
3019                                 MSG_WriteByte(&net_message, CCREP_RULE_INFO);
3020                                 if (var)
3021                                 {
3022                                         MSG_WriteString(&net_message, var->name);
3023                                         MSG_WriteString(&net_message, var->string);
3024                                 }
3025                                 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
3026                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
3027                                 SZ_Clear(&net_message);
3028                         }
3029                         break;
3030                 default:
3031                         break;
3032                 }
3033                 SZ_Clear(&net_message);
3034                 // we may not have liked the packet, but it was a valid control
3035                 // packet, so we're done processing this packet now
3036                 return true;
3037         }
3038         if (host_client)
3039         {
3040                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->spawned ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3041                 {
3042                         SV_VM_Begin();
3043                         SV_ReadClientMessage();
3044                         SV_VM_End();
3045                         return ret;
3046                 }
3047         }
3048         return 0;
3049 }
3050
3051 void NetConn_ServerFrame(void)
3052 {
3053         int i, length;
3054         lhnetaddress_t peeraddress;
3055         for (i = 0;i < sv_numsockets;i++)
3056                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3057                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3058         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3059         {
3060                 // never timeout loopback connections
3061                 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3062                 {
3063                         Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3064                         SV_VM_Begin();
3065                         SV_DropClient(false);
3066                         SV_VM_End();
3067                 }
3068         }
3069 }
3070
3071 void NetConn_SleepMicroseconds(int microseconds)
3072 {
3073         LHNET_SleepUntilPacket_Microseconds(microseconds);
3074 }
3075
3076 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3077 {
3078         int i, j;
3079         int masternum;
3080         lhnetaddress_t masteraddress;
3081         lhnetaddress_t broadcastaddress;
3082         char request[256];
3083
3084         if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3085                 return;
3086
3087         // 26000 is the default quake server port, servers on other ports will not
3088         // be found
3089         // note this is IPv4-only, I doubt there are IPv6-only LANs out there
3090         LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3091
3092         if (querydp)
3093         {
3094                 for (i = 0;i < cl_numsockets;i++)
3095                 {
3096                         if (cl_sockets[i])
3097                         {
3098                                 const char *cmdname, *extraoptions;
3099                                 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3100
3101                                 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3102                                 {
3103                                         // search LAN for Quake servers
3104                                         SZ_Clear(&net_message);
3105                                         // save space for the header, filled in later
3106                                         MSG_WriteLong(&net_message, 0);
3107                                         MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
3108                                         MSG_WriteString(&net_message, "QUAKE");
3109                                         MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
3110                                         StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
3111                                         NetConn_Write(cl_sockets[i], net_message.data, net_message.cursize, &broadcastaddress);
3112                                         SZ_Clear(&net_message);
3113
3114                                         // search LAN for DarkPlaces servers
3115                                         NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3116                                 }
3117
3118                                 // build the getservers message to send to the dpmaster master servers
3119                                 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3120                                 {
3121                                         cmdname = "getserversExt";
3122                                         extraoptions = " ipv4 ipv6";  // ask for IPv4 and IPv6 servers
3123                                 }
3124                                 else
3125                                 {
3126                                         cmdname = "getservers";
3127                                         extraoptions = "";
3128                                 }
3129                                 dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
3130
3131                                 // search internet
3132                                 for (masternum = 0;sv_masters[masternum].name;masternum++)
3133                                 {
3134                                         if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3135                                         {
3136                                                 masterquerycount++;
3137                                                 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3138                                         }
3139                                 }
3140
3141                                 // search favorite servers
3142                                 for(j = 0; j < nFavorites; ++j)
3143                                 {
3144                                         if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3145                                         {
3146                                                 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3147                                                         NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3148                                         }
3149                                 }
3150                         }
3151                 }
3152         }
3153
3154         // only query QuakeWorld servers when the user wants to
3155         if (queryqw)
3156         {
3157                 for (i = 0;i < cl_numsockets;i++)
3158                 {
3159                         if (cl_sockets[i])
3160                         {
3161                                 int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3162
3163                                 if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3164                                 {
3165                                         // search LAN for QuakeWorld servers
3166                                         NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3167
3168                                         // build the getservers message to send to the qwmaster master servers
3169                                         // note this has no -1 prefix, and the trailing nul byte is sent
3170                                         dpsnprintf(request, sizeof(request), "c\n");
3171                                 }
3172
3173                                 // search internet
3174                                 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3175                                 {
3176                                         if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3177                                         {
3178                                                 if (m_state != m_slist)
3179                                                 {
3180                                                         char lookupstring[128];
3181                                                         LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3182                                                         Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3183                                                 }
3184                                                 masterquerycount++;
3185                                                 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3186                                         }
3187                                 }
3188
3189                                 // search favorite servers
3190                                 for(j = 0; j < nFavorites; ++j)
3191                                 {
3192                                         if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3193                                         {
3194                                                 if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3195                                                 {
3196                                                         NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3197                                                         NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3198                                                 }
3199                                         }
3200                                 }
3201                         }
3202                 }
3203         }
3204         if (!masterquerycount)
3205         {
3206                 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3207                 M_Update_Return_Reason("No network");
3208         }
3209 }
3210
3211 void NetConn_Heartbeat(int priority)
3212 {
3213         lhnetaddress_t masteraddress;
3214         int masternum;
3215         lhnetsocket_t *mysocket;
3216
3217         // if it's a state change (client connected), limit next heartbeat to no
3218         // more than 30 sec in the future
3219         if (priority == 1 && nextheartbeattime > realtime + 30.0)
3220                 nextheartbeattime = realtime + 30.0;
3221
3222         // limit heartbeatperiod to 30 to 270 second range,
3223         // lower limit is to avoid abusing master servers with excess traffic,
3224         // upper limit is to avoid timing out on the master server (which uses
3225         // 300 sec timeout)
3226         if (sv_heartbeatperiod.value < 30)
3227                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3228         if (sv_heartbeatperiod.value > 270)
3229                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3230
3231         // make advertising optional and don't advertise singleplayer games, and
3232         // only send a heartbeat as often as the admin wants
3233         if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3234         {
3235                 nextheartbeattime = realtime + sv_heartbeatperiod.value;
3236                 for (masternum = 0;sv_masters[masternum].name;masternum++)
3237                         if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3238                                 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3239         }
3240 }
3241
3242 static void Net_Heartbeat_f(void)
3243 {
3244         if (sv.active)
3245                 NetConn_Heartbeat(2);
3246         else
3247                 Con_Print("No server running, can not heartbeat to master server.\n");
3248 }
3249
3250 void PrintStats(netconn_t *conn)
3251 {
3252         if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3253                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3254         else
3255                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3256 }
3257
3258 void Net_Stats_f(void)
3259 {
3260         netconn_t *conn;
3261         Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
3262         Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
3263         Con_Printf("reliable messages sent     = %i\n", reliableMessagesSent);
3264         Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
3265         Con_Printf("packetsSent                = %i\n", packetsSent);
3266         Con_Printf("packetsReSent              = %i\n", packetsReSent);
3267         Con_Printf("packetsReceived            = %i\n", packetsReceived);
3268         Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
3269         Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
3270         Con_Print("connections                =\n");
3271         for (conn = netconn_list;conn;conn = conn->next)
3272                 PrintStats(conn);
3273 }
3274
3275 void Net_Refresh_f(void)
3276 {
3277         if (m_state != m_slist) {
3278                 Con_Print("Sending new requests to master servers\n");
3279                 ServerList_QueryList(false, true, false, true);
3280                 Con_Print("Listening for replies...\n");
3281         } else
3282                 ServerList_QueryList(false, true, false, false);
3283 }
3284
3285 void Net_Slist_f(void)
3286 {
3287         ServerList_ResetMasks();
3288         serverlist_sortbyfield = SLIF_PING;
3289         serverlist_sortflags = 0;
3290     if (m_state != m_slist) {
3291                 Con_Print("Sending requests to master servers\n");
3292                 ServerList_QueryList(true, true, false, true);
3293                 Con_Print("Listening for replies...\n");
3294         } else
3295                 ServerList_QueryList(true, true, false, false);
3296 }
3297
3298 void Net_SlistQW_f(void)
3299 {
3300         ServerList_ResetMasks();
3301         serverlist_sortbyfield = SLIF_PING;
3302         serverlist_sortflags = 0;
3303     if (m_state != m_slist) {
3304                 Con_Print("Sending requests to master servers\n");
3305                 ServerList_QueryList(true, false, true, true);
3306                 serverlist_consoleoutput = true;
3307                 Con_Print("Listening for replies...\n");
3308         } else
3309                 ServerList_QueryList(true, false, true, false);
3310 }
3311
3312 void NetConn_Init(void)
3313 {
3314         int i;
3315         lhnetaddress_t tempaddress;
3316         netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3317         Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3318         Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3319         Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3320         Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3321         Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3322         Cvar_RegisterVariable(&rcon_restricted_password);
3323         Cvar_RegisterVariable(&rcon_restricted_commands);
3324         Cvar_RegisterVariable(&rcon_secure_maxdiff);
3325         Cvar_RegisterVariable(&net_slist_queriespersecond);
3326         Cvar_RegisterVariable(&net_slist_queriesperframe);
3327         Cvar_RegisterVariable(&net_slist_timeout);
3328         Cvar_RegisterVariable(&net_slist_maxtries);
3329         Cvar_RegisterVariable(&net_slist_favorites);
3330         Cvar_RegisterVariable(&net_slist_pause);
3331         Cvar_RegisterVariable(&net_messagetimeout);
3332         Cvar_RegisterVariable(&net_connecttimeout);
3333         Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3334         Cvar_RegisterVariable(&cl_netlocalping);
3335         Cvar_RegisterVariable(&cl_netpacketloss_send);
3336         Cvar_RegisterVariable(&cl_netpacketloss_receive);
3337         Cvar_RegisterVariable(&hostname);
3338         Cvar_RegisterVariable(&developer_networking);
3339         Cvar_RegisterVariable(&cl_netport);
3340         Cvar_RegisterVariable(&sv_netport);
3341         Cvar_RegisterVariable(&net_address);
3342         Cvar_RegisterVariable(&net_address_ipv6);
3343         Cvar_RegisterVariable(&sv_public);
3344         Cvar_RegisterVariable(&sv_heartbeatperiod);
3345         for (i = 0;sv_masters[i].name;i++)
3346                 Cvar_RegisterVariable(&sv_masters[i]);
3347         Cvar_RegisterVariable(&gameversion);
3348         Cvar_RegisterVariable(&gameversion_min);
3349         Cvar_RegisterVariable(&gameversion_max);
3350 // COMMANDLINEOPTION: Server: -ip <ipaddress> sets the ip address of this machine for purposes of networking (default 0.0.0.0 also known as INADDR_ANY), use only if you have multiple network adapters and need to choose one specifically.
3351         if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3352         {
3353                 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3354                 {
3355                         Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3356                         Cvar_SetQuick(&net_address, com_argv[i + 1]);
3357                 }
3358                 else
3359                         Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3360         }
3361 // COMMANDLINEOPTION: Server: -port <portnumber> sets the port to use for a server (default 26000, the same port as QUAKE itself), useful if you host multiple servers on your machine
3362         if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3363         {
3364                 i = atoi(com_argv[i + 1]);
3365                 if (i >= 0 && i < 65536)
3366                 {
3367                         Con_Printf("-port option used, setting port cvar to %i\n", i);
3368                         Cvar_SetValueQuick(&sv_netport, i);
3369                 }
3370                 else
3371                         Con_Printf("-port option used, but %i is not a valid port number\n", i);
3372         }
3373         cl_numsockets = 0;
3374         sv_numsockets = 0;
3375         net_message.data = net_message_buf;
3376         net_message.maxsize = sizeof(net_message_buf);
3377         net_message.cursize = 0;
3378         LHNET_Init();
3379 }
3380
3381 void NetConn_Shutdown(void)
3382 {
3383         NetConn_CloseClientPorts();
3384         NetConn_CloseServerPorts();
3385         LHNET_Shutdown();
3386 }
3387