1 /* XQF - Quake server browser and launcher
2  * Copyright (C) 1998-2000 Roman Pozlevich <roma@botik.ru>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (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.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
17  */
18 
19 #include <sys/types.h>
20 #include <stdio.h>      /* fprintf */
21 #include <string.h>     /* strchr, strncpy */
22 #include <stdlib.h>     /* strtol */
23 #include <sys/socket.h> /* inet_ntoa */
24 #include <netinet/in.h> /* inet_ntoa */
25 #include <arpa/inet.h>  /* inet_ntoa */
26 
27 #include <glib.h>
28 
29 #include "xqf.h"
30 #include "game.h"
31 #include "host.h"
32 #include "utils.h"
33 #include "server.h"
34 
35 #ifdef USE_GEOIP
36 #include "country-filter.h"
37 #endif
38 
39 
40 struct server_hash {
41 	int num;
42 	GSList **nodes;
43 };
44 
45 static struct server_hash servers = { 2333, NULL };
46 static struct server_hash uservers = { 251, NULL };
47 
48 
server_hash_func(const struct host * h,unsigned short port)49 static int server_hash_func (const struct host *h, unsigned short port) {
50 	unsigned char *ptr;
51 
52 	if (!h)
53 		return 0;
54 
55 	ptr = (unsigned char *) &h->ip.s_addr;
56 	return (ptr[0] + (ptr[1] << 2) + (ptr[2] << 4) + (ptr[3] << 6) + port) %
57 		servers.num;
58 }
59 
60 
userver_hash_func(const char * hostname,unsigned short port)61 static int userver_hash_func (const char *hostname, unsigned short port) {
62 	int sum = 0;
63 
64 	while (*hostname) {
65 		sum += *hostname;
66 		hostname++;
67 	}
68 	return (sum + port) % uservers.num;
69 }
70 
71 
72 
73 /*
74    server_new -- Create (malloc) a new server structure with
75    all of the values set at zero except the reference count which
76    should be at one.
77 */
78 
server_new(struct host * h,unsigned short port,enum server_type type)79 static struct server *server_new (struct host *h, unsigned short port,
80 		enum server_type type) {
81 	struct server *server;
82 
83 	if (port == 0 || type == UNKNOWN_SERVER)
84 		return NULL;
85 
86 	server = g_malloc0 (sizeof (struct server));
87 	debug (6, "server_new() -- Server %lx", server);
88 
89 	server_ref (server);
90 
91 	server->host = h;
92 	host_ref (h); /* Increse the refernece count on the host struct */
93 
94 	server->port = port;
95 	server->type = type;
96 	server->ping = -1;
97 	server->retries = -1;
98 
99 #ifdef USE_GEOIP
100 	server->country_id = geoip_id_by_ip(h->ip);
101 #endif
102 
103 	return server;
104 }
105 
106 
userver_new(const char * hostname,unsigned short port,enum server_type type)107 static struct userver *userver_new (const char *hostname, unsigned short port,
108 		enum server_type type) {
109 	struct userver *userver;
110 
111 	if (port == 0 || type == UNKNOWN_SERVER)
112 		return NULL;
113 
114 	userver = g_malloc0 (sizeof (struct userver));
115 	userver_ref (userver);  /* baa -- Needed? FIX ME */
116 	userver->hostname = g_strdup (hostname);
117 	userver->port = port;
118 	userver->type = type;
119 
120 	return userver;
121 }
122 
123 
124 
125 /*
126    server_add -- See if a host/port is in our list.  If it is not
127    then add one.  If it is then we increase the reference count.
128 */
server_add(struct host * h,unsigned short port,enum server_type type)129 struct server *server_add (struct host *h, unsigned short port,
130 		enum server_type type) {
131 	struct server *server;
132 	GSList *ptr;
133 	int node;
134 
135 	if (!h || type == UNKNOWN_SERVER)
136 		return NULL;
137 
138 	if (port == 0)
139 		port = games[type].default_port;
140 
141 	node = server_hash_func (h, port);
142 
143 	debug (6, "server_add() -- Add/Get a server");
144 
145 	if (!servers.nodes) {
146 		servers.nodes = g_malloc0 (sizeof (GSList *) * servers.num);
147 	}
148 	else {
149 		/* Go through each of the servers in the list (each node)
150 		   and see if we have a matching (by reference) entry.
151 		*/
152 		for (ptr = servers.nodes[node]; ptr; ptr = ptr->next) {
153 			server = (struct server *) ptr->data;
154 			if (server->host == h && server->port == port) {
155 				server_ref (server);   /* baa -- added Dec-27, 2000 CORE FIX? */
156 				return server;
157 			}
158 		}
159 	}
160 
161 	server = server_new (h, port, type);
162 	if (server) {
163 		servers.nodes[node] = g_slist_prepend (servers.nodes[node], server);
164 
165 		if (games[type].analyze_serverinfo)
166 			(*games[type].analyze_serverinfo) (server);
167 	}
168 	return server;
169 }
170 
userver_add(const char * hostname,unsigned short port,enum server_type type)171 struct userver *userver_add (const char *hostname, unsigned short port,
172 		enum server_type type) {
173 	GSList *ptr;
174 	struct userver *s;
175 	int node;
176 
177 	if (!hostname || type == UNKNOWN_SERVER)
178 		return NULL;
179 
180 	if (port == 0)
181 		port = games[type].default_port;
182 
183 	node = userver_hash_func (hostname, port);
184 
185 	if (!uservers.nodes) {
186 		uservers.nodes = g_malloc0 (sizeof (GSList *) * uservers.num);
187 	}
188 	else {
189 		for (ptr = uservers.nodes[node]; ptr; ptr = ptr->next) {
190 			s = (struct userver *) ptr->data;
191 			if (strcmp (s->hostname, hostname) == 0 && s->port == port)
192 				return s;
193 		}
194 	}
195 
196 	s = userver_new (hostname, port, type);
197 	if (s)
198 		uservers.nodes[node] = g_slist_prepend (uservers.nodes[node], s);
199 	return s;
200 }
201 
202 
server_free_info(struct server * s)203 void server_free_info (struct server *s) {
204 	if (s->name) {
205 		g_free (s->name);
206 		s->name = NULL;
207 	}
208 	if (s->map) {
209 		g_free (s->map);
210 		s->map  = NULL;
211 	}
212 	if (s->info) {
213 		g_free (s->info);
214 		s->info = NULL;
215 		s->game = NULL;
216 	}
217 
218 	if (s->gametype) { /* Added by baa */
219 		s->gametype = NULL;
220 	}
221 
222 	if (s->players) {
223 		g_slist_foreach (s->players, (GFunc) g_free, NULL);
224 		g_slist_free (s->players);
225 		s->players = NULL;
226 	}
227 
228 	s->flags = 0;
229 	s->maxplayers = 0;
230 	s->curplayers = 0;
231 	s->curbots = 0;
232 
233 	s->filters = 0;
234 	s->flt_mask = 0;
235 	s->flt_last = 0;
236 }
237 
238 
239 // return NULL if refcount dropped to zero, server otherwise
server_unref(struct server * server)240 struct server* server_unref (struct server *server) {
241 	int node;
242 
243 	if (!server)
244 		return NULL;
245 
246 	server->ref_count--;
247 
248 	debug (7, "server_unref() -- Server %lx ref now at %d",
249 			server, server->ref_count);
250 
251 	if (server->ref_count <= 0) {
252 		node = server_hash_func (server->host, server->port);
253 		servers.nodes[node] = g_slist_remove (servers.nodes[node], server);
254 		/*
255 		   Oops, it seems that we were freeing the server info before
256 		   freeing the host info.  Bad. To free the host info w/o a memory
257 		   leak we need to do that before freeing the server info. --baa
258 		*/
259 		host_unref (server->host);
260 		server_free_info (server);
261 		g_free (server);
262 		return NULL;
263 	}
264 	return server;
265 }
266 
267 
userver_unref(struct userver * s)268 void userver_unref (struct userver *s) {
269 	int node;
270 
271 	if (!s)
272 		return;
273 
274 	s->ref_count--;
275 
276 	debug (6, "userver_unref() -- UServer %lx ref now at %d",
277 			s, s->ref_count);
278 
279 	if (s->ref_count <= 0) {
280 		node = userver_hash_func (s->hostname, s->port);
281 		uservers.nodes[node] = g_slist_remove (uservers.nodes[node], s);
282 		if (s->s) server_unref (s->s);
283 		g_free (s->hostname);
284 		g_free (s);
285 	}
286 }
287 
288 // change the server port to newport. this function is required as the server
289 // needs to get a new position in the servers hash
server_change_port(struct server * s,int newport)290 struct server* server_change_port (struct server* s, int newport) {
291 	int node;
292 
293 	if (!newport || !s)
294 		return s;
295 
296 	node = server_hash_func (s->host, s->port);
297 	servers.nodes[node] = g_slist_remove (servers.nodes[node], s);
298 	s->port = newport;
299 	node = server_hash_func (s->host, s->port);
300 	// FIXME: there could be already a server with that port
301 	servers.nodes[node] = g_slist_prepend (servers.nodes[node], s);
302 	return s;
303 }
304 
server_list_copy(GSList * list)305 GSList *server_list_copy (GSList *list) {
306 	debug (3, "server_list_copy() -- list %ld copying all servers", list);
307 	g_slist_foreach (list, (GFunc) server_ref, NULL);
308 	return g_slist_copy (list);
309 }
310 
311 
userver_list_copy(GSList * list)312 GSList *userver_list_copy (GSList *list) {
313 	g_slist_foreach (list, (GFunc) userver_ref, NULL);
314 	return g_slist_copy (list);
315 }
316 
317 
server_list_append_list(GSList * list,GSList * server_list,enum server_type type)318 GSList *server_list_append_list (GSList *list, GSList *server_list,
319 		enum server_type type) {
320 	struct server *server;
321 	GSList *add = NULL;
322 	debug (6, "server_list_append_list() -- list %lx", list);
323 	while (server_list) {
324 		server = (struct server *) server_list->data;
325 		if (type == UNKNOWN_SERVER || type == server->type) {
326 			if (g_slist_find (list, server) == NULL) {
327 				add = g_slist_prepend (add, server);
328 				server_ref (server);
329 			}
330 		}
331 		server_list = g_slist_next (server_list);
332 	}
333 
334 	return g_slist_concat (list, g_slist_reverse (add));
335 }
336 
337 
userver_list_append_list(GSList * list,GSList * uservers,enum server_type type)338 GSList *userver_list_append_list (GSList *list, GSList *uservers,
339 		enum server_type type) {
340 	struct userver *us;
341 	GSList *add = NULL;
342 
343 	while (uservers) {
344 		us = (struct userver *) uservers->data;
345 		if (type == UNKNOWN_SERVER || type == us->type) {
346 			if (g_slist_find (list, us) == NULL) {
347 				add = g_slist_prepend (add, us);
348 				userver_ref (us);
349 			}
350 		}
351 		uservers = g_slist_next (uservers);
352 	}
353 
354 	return g_slist_concat (list, g_slist_reverse (add));
355 }
356 
357 
servers_total(void)358 int servers_total (void) {
359 	int i;
360 	int size = 0, len;
361 	int min=-1, max=0;
362 
363 	if (!servers.nodes)
364 		return 0;
365 
366 	for (i = 0; i < servers.num; i++) {
367 		len = g_slist_length (servers.nodes[i]);
368 		size += len;
369 		if (min == -1 || len < min) min=len;
370 		if (len > max) max=len;
371 	}
372 
373 	debug(1,"server hash (min/max/avg) %d/%d/%5.2f",min,max,size/(servers.num*1.0));
374 
375 	return size;
376 }
377 
378 
uservers_total(void)379 int uservers_total (void) {
380 	int i;
381 	int size = 0;
382 
383 	if (!uservers.nodes)
384 		return 0;
385 
386 	for (i = 0; i < uservers.num; i++)
387 		size += g_slist_length (uservers.nodes[i]);
388 
389 	return size;
390 }
391 
392 
393 
394 /*
395    all_servers -- Get a list of all servers.  This is called from
396    two functions source.c:free_masters and statistics.c:collect_statistics
397    both of which call server_free_list afterwards.
398 */
all_servers(void)399 GSList *all_servers (void) {
400 	GSList *list = NULL;
401 	GSList *tmp;
402 	struct server *server;
403 	int i;
404 
405 	if (!servers.nodes)
406 		return NULL;
407 	debug (6, "all_servers() -- Get all servers");
408 	for (i = 0; i < servers.num; i++) {
409 		for (tmp = servers.nodes[i]; tmp; tmp = tmp->next) {
410 			server = (struct server *) tmp->data;
411 			list = g_slist_prepend (list, server);
412 			server_ref (server);
413 		}
414 	}
415 
416 	return list;
417 }
418 
419 
parse_address(char * str,char ** addr,unsigned short * port)420 int parse_address (char *str, char **addr, unsigned short *port) {
421 	long tmp;
422 	char *ptr;
423 	char *endptr;
424 
425 	if (!str || !addr || !port)
426 		return FALSE;
427 
428 	*port = 0;
429 	*addr = NULL;
430 
431 	ptr = strchr (str, ':');
432 	if (!ptr) {
433 		if (hostname_is_valid (str)) {
434 			*addr = g_strdup (str);
435 			return TRUE;
436 		}
437 		return FALSE;
438 	}
439 
440 	if (ptr == str)
441 		return FALSE;
442 
443 	tmp = strtol (ptr + 1, &endptr, 10);
444 
445 	if (*endptr != '\0' || tmp <= 0 || tmp > 65535)
446 		return FALSE;
447 
448 	*port = (unsigned short) tmp;
449 
450 	/* malloc(strlen("addr:port") - strlen(":port") + strlen("\0")); */
451 	*addr = g_malloc (strlen(str) - strlen(ptr) + 1);
452 	strncpy (*addr, str, ptr - str);
453 	(*addr) [ptr - str] = '\0';
454 
455 	if (hostname_is_valid (*addr))
456 		return TRUE;
457 
458 	g_free (*addr);
459 	*addr = NULL;
460 	*port = 0;
461 	return FALSE;
462 }
463 
464 
userver_set_host(struct userver * us,struct host * h)465 struct server *userver_set_host (struct userver *us, struct host *h) {
466 	struct server *server;
467 
468 	if (!us || !h) return NULL;
469 	if (us->s) return us->s;
470 
471 	/* Since server_add increases the reference count for us,
472 	   we do not do it here. */
473 	server = server_add (h, us->port, us->type);
474 	if (server) {
475 		us->s = server;
476 	}
477 
478 	return server;
479 }
480 
481 
uservers_to_servers(GSList ** uservers,GSList ** servers)482 void uservers_to_servers (GSList **uservers, GSList **servers) {
483 	struct userver *us;
484 	GSList *tmp = *uservers;
485 
486 	debug (6, "uservers_to_servers() -- uservers %lx  tmp %lx", uservers, tmp);
487 	while (tmp) {
488 		us = (struct userver *) tmp->data;
489 		debug (7, "uservers_to_servers() -- Check userver %lx", us);
490 		if (us->s) {
491 			debug (7, "uservers_to_servers() -- server %lx  userver %lx", us->s, us);
492 			*servers = server_list_append (*servers, us->s);
493 
494 			*uservers = g_slist_remove (*uservers, us);
495 			userver_unref (us);
496 
497 			tmp = *uservers;
498 			continue;
499 		}
500 		tmp = tmp->next;
501 	}
502 	debug (6, "uservers_to_servers() -- Done.");
503 }
504 
505 
506 /*
507    server_lists_intersect() -- Find servers that are
508    in two lists (by reference).  The first arg gets a list
509    of servers not in both lists.  The second list gets servers
510    removed that are in both lists.
511 */
512 
server_lists_intersect(GSList ** list1,GSList ** list2)513 void server_lists_intersect (GSList **list1, GSList **list2) {
514 	GSList *list3 = NULL;
515 	GSList *tmp;
516 	struct server *s;
517 
518 	debug (6, "server_lists_intersect() -- ");
519 	for (tmp = *list1; tmp; tmp = tmp->next) {
520 		s = (struct server *) tmp->data;
521 		if (g_slist_find (*list2, s)) {
522 			*list2 = g_slist_remove (*list2, s);
523 			server_unref (s);
524 		}
525 		else {
526 			list3 = g_slist_prepend (list3, s);
527 			server_ref (s);
528 		}
529 	}
530 
531 	server_list_free (*list1);
532 	*list1 = list3;
533 }
534 
535 
server_list_fprintf(FILE * f,GSList * servers)536 void server_list_fprintf (FILE *f, GSList *servers) {
537 	struct server *s;
538 	int i;
539 
540 	for (i = 0; servers; i++) {
541 		s = (struct server *) servers->data;
542 		fprintf (f, "%d> [%s:%d](%d) %s \"%s\"\n", i, inet_ntoa (s->host->ip),
543 				s->port, s->ref_count, games[s->type].id, s->name);
544 		servers = servers->next;
545 	}
546 }
547 
548 
userver_list_fprintf(FILE * f,GSList * uservers)549 void userver_list_fprintf (FILE *f, GSList *uservers) {
550 	struct userver *us;
551 	int i;
552 
553 	for (i = 0; uservers; i++) {
554 		us = (struct userver *) uservers->data;
555 		fprintf (f, "%d> [%s:%d](%d) %s\n", i, us->hostname, us->port,
556 				us->ref_count, games[us->type].id);
557 		if (us->s) {
558 			fprintf (f, "  --> [%s:%d](%d) %s \"%s\"\n",
559 					inet_ntoa (us->s->host->ip), us->s->port,
560 					us->s->ref_count, games[us->type].id, us->s->name);
561 		}
562 
563 		uservers = uservers->next;
564 	}
565 }
566 
server_set_env(const struct server * s)567 void server_set_env(const struct server* s) {
568 	char buf[256];
569 
570 	if (!s) return;
571 
572 	if (s->flags & SERVER_PUNKBUSTER)
573 		setenv("XQF_SERVER_ANTICHEAT", "1", 1);
574 
575 	if (s->name)
576 		setenv("XQF_SERVER_NAME", s->name, 1);
577 
578 	if (s->map)
579 		setenv("XQF_SERVER_MAP", s->map, 1);
580 
581 	if (s->host->name)
582 		setenv("XQF_SERVER_HOSTNAME", s->host->name, 1);
583 
584 	if (s->game)
585 		setenv("XQF_SERVER_GAME", s->game, 1);
586 
587 	setenv("XQF_SERVER_IP", inet_ntoa (s->host->ip), 1);
588 
589 	snprintf(buf, sizeof(buf), "%d", s->port);
590 	setenv("XQF_SERVER_PORT", buf, 1);
591 
592 	setenv("XQF_GAME_TYPE", games[s->type].id, 1);
593 
594 	if (games[s->type].flags & GAME_LAUNCH_HOSTPORT) {
595 		char **info_ptr;
596 		// go through all server rules
597 		for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
598 			if (!strcmp (*info_ptr, "hostport")) {
599 				setenv("XQF_SERVER_HOSTPORT", info_ptr[1], 1);
600 			}
601 		}
602 	}
603 }
604