The call to alloca has been removed. This function has a quite bad reputation and...
[divverent/darkplaces.git] / net_master.c
1 /*
2 Copyright (C) 2002 Mathieu Olivier
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // net_master.c
21
22 #include "quakedef.h"
23
24 cvar_t sv_masters [] =
25 {
26         {CVAR_SAVE, "sv_master1", ""},
27         {CVAR_SAVE, "sv_master2", ""},
28         {CVAR_SAVE, "sv_master3", ""},
29         {CVAR_SAVE, "sv_master4", ""}
30 };
31
32
33 /*
34 ====================
35 Master_BuildGetServers
36
37 Build a getservers request for a master server
38 ====================
39 */
40 char* Master_BuildGetServers (void)
41 {
42         static int nextmaster = 0;
43         cvar_t* sv_master;
44         char request [256];
45         
46         if (nextmaster >= sizeof (sv_masters) / sizeof (sv_masters[0]))
47         {
48                 nextmaster = 0;
49                 return NULL;
50         }
51
52         sv_master = &sv_masters[nextmaster++];
53
54         // No master, no heartbeat
55         if (sv_master->string[0] == '\0')
56         {
57                 nextmaster = 0;
58                 return NULL;
59         }
60         
61         // Build the heartbeat
62         snprintf (request, sizeof (request), "getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
63         SZ_Clear (&net_message);
64         MSG_WriteLong (&net_message, -1);
65         MSG_WriteString (&net_message, request);
66
67         net_message.cursize--;  // we don't send the trailing '\0'
68
69         return sv_master->string;
70 }
71
72
73 /*
74 ====================
75 Master_BuildHeartbeat
76
77 Build an heartbeat for a master server
78 ====================
79 */
80 char* Master_BuildHeartbeat (void)
81 {
82         static int nextmaster = 0;
83         cvar_t* sv_master;
84         
85         if (nextmaster >= sizeof (sv_masters) / sizeof (sv_masters[0]))
86         {
87                 nextmaster = 0;
88                 return NULL;
89         }
90
91         sv_master = &sv_masters[nextmaster++];
92
93         // No master, no heartbeat
94         if (sv_master->string[0] == '\0')
95         {
96                 nextmaster = 0;
97                 return NULL;
98         }
99         
100         // Build the heartbeat
101         SZ_Clear (&net_message);
102         MSG_WriteLong (&net_message, -1);
103         MSG_WriteString (&net_message, "heartbeat DarkPlaces\x0A");
104
105         net_message.cursize--;  // we don't send the trailing '\0'
106
107         return sv_master->string;
108 }
109
110
111 /*
112 ====================
113 Master_HandleMessage
114
115 Handle the master server messages
116 ====================
117 */
118 int Master_HandleMessage (void)
119 {
120         const char* string = MSG_ReadString ();
121
122         // If it's a "getinfo" request
123         if (!strncmp (string, "getinfo", 7))
124         {
125                 char response [512];
126                 size_t length;
127
128                 length = snprintf (response, sizeof (response), "infoResponse\x0A"
129                                         "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
130                                         "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d",
131                                         gamename, com_modname, svs.maxclients, net_activeconnections,
132                                         sv.name, hostname.string, NET_PROTOCOL_VERSION);
133
134                 // Too long to fit into the buffer?
135                 if (length >= sizeof (response))
136                         return -1;
137
138                 // If there was a challenge in the getinfo message
139                 if (string[7] == ' ')
140                 {
141                         string += 8;  // skip the header and the space
142
143                         // If the challenge wouldn't fit into the buffer
144                         if (length + 11 + strlen (string) >= sizeof (response))
145                                 return -1;
146
147                         sprintf (response + length, "\\challenge\\%s", string);
148                 }
149
150                 SZ_Clear (&net_message);
151                 MSG_WriteLong (&net_message, -1);
152                 MSG_WriteString (&net_message, response);
153
154                 return net_message.cursize - 1;
155         }
156
157         return 0;
158 }
159
160
161 /*
162 ====================
163 Master_Init
164
165 Initialize the code that handles master server requests and reponses
166 ====================
167 */
168 void Master_Init (void)
169 {
170         unsigned int ind;
171         for (ind = 0; ind < sizeof (sv_masters) / sizeof (sv_masters[0]); ind++)
172                 Cvar_RegisterVariable (&sv_masters[ind]);
173 }
174
175
176 /*
177 ====================
178 Master_ParseServerList
179
180 Parse getserverResponse messages
181 ====================
182 */
183 void Master_ParseServerList (net_landriver_t* dfunc)
184 {
185         int control;
186         qbyte* servers;
187         qbyte* crtserver;
188         int ipaddr;
189         struct qsockaddr svaddr;
190         char ipstring [32];
191
192         if (net_message.cursize < sizeof(int))
193                 return;
194
195         // is the cache full?
196         if (hostCacheCount == HOSTCACHESIZE)
197                 return;
198
199         MSG_BeginReading ();
200         control = BigLong(*((int *)net_message.data));
201         MSG_ReadLong();
202         if (control != -1)
203                 return;
204
205         if (strncmp (net_message.data + 4, "getserversResponse\\", 19)) 
206                 return;
207
208         // Skip the next 19 bytes
209         MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong();
210         MSG_ReadShort(); MSG_ReadByte();
211
212         crtserver = servers = Z_Malloc (net_message.cursize - 23);
213         memcpy (servers , net_message.data + 23, net_message.cursize - 23);
214
215         // Extract the IP addresses
216         while ((ipaddr = (crtserver[3] << 24) | (crtserver[2] << 16) | (crtserver[1] << 8) | crtserver[0]) != -1)
217         {
218                 int port = (crtserver[5] << 8) | crtserver[4];
219
220                 if (port == -1 || port == 0)
221                         break;
222
223                 port = ((port >> 8) & 0xFF) + ((port & 0xFF) << 8);
224                 sprintf (ipstring, "%u.%u.%u.%u:%hu",
225                                         ipaddr & 0xFF, (ipaddr >> 8) & 0xFF,
226                                         (ipaddr >> 16) & 0xFF, ipaddr >> 24,
227                                         port);
228                 dfunc->GetAddrFromName (ipstring, &svaddr);
229
230                 Con_DPrintf("Requesting info from server %s\n", ipstring);
231                 // Send a request at this address
232                 SZ_Clear(&net_message);
233                 MSG_WriteLong(&net_message, 0);  // save space for the header, filled in later
234                 MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
235                 MSG_WriteString(&net_message, "QUAKE");
236                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
237                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
238                 dfunc->Write(dfunc->controlSock, net_message.data, net_message.cursize, &svaddr);
239                 SZ_Clear(&net_message);
240
241                 if (crtserver[6] != '\\')
242                         break;
243                 crtserver += 7;
244         }
245
246         Z_Free (servers);
247 }