1 /*
2 master.c
3
4 A master server for Tremulous
5
6 Copyright (C) 2002-2005 Mathieu Olivier
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23
24 #include <stdarg.h>
25 #include <signal.h>
26
27 #ifndef WIN32
28 # include <pwd.h>
29 # include <unistd.h>
30 #endif
31
32 #include "common.h"
33 #include "messages.h"
34 #include "servers.h"
35
36
37 // ---------- Constants ---------- //
38
39 // Version of dpmaster
40 #define VERSION "1.6"
41
42 // Default master port
43 #define DEFAULT_MASTER_PORT 30710
44
45 // Maximum and minimum sizes for a valid packet
46 #define MAX_PACKET_SIZE 2048
47 #define MIN_PACKET_SIZE 5
48
49 #ifndef WIN32
50 // Default path we use for chroot
51 # define DEFAULT_JAIL_PATH "/var/empty/"
52
53 // User we use by default for dropping super-user privileges
54 # define DEFAULT_LOW_PRIV_USER "nobody"
55 #endif
56
57
58 // ---------- Types ---------- //
59
60 #ifdef WIN32
61 typedef int socklen_t;
62 #endif
63
64
65 // ---------- Private variables ---------- //
66
67 // The port we use
68 static unsigned short master_port = DEFAULT_MASTER_PORT;
69
70 // Local address we listen on, if any
71 static const char* listen_name = NULL;
72 static struct in_addr listen_addr;
73
74 #ifndef WIN32
75 // On UNIX systems, we can run as a daemon
76 static qboolean daemon_mode = qfalse;
77
78 // Path we use for chroot
79 static const char* jail_path = DEFAULT_JAIL_PATH;
80
81 // Low privileges user
82 static const char* low_priv_user = DEFAULT_LOW_PRIV_USER;
83 #endif
84
85
86 // ---------- Public variables ---------- //
87
88 // The master socket
89 int sock = -1;
90
91 // The current time (updated every time we receive a packet)
92 time_t crt_time;
93
94 // Maximum level for a message to be printed
95 msg_level_t max_msg_level = MSG_NORMAL;
96
97 // Peer address. We rebuild it every time we receive a new packet
98 char peer_address [128];
99
100
101 // ---------- Private functions ---------- //
102
103 /*
104 ====================
105 PrintPacket
106
107 Print the contents of a packet on stdout
108 ====================
109 */
PrintPacket(const char * packet,size_t length)110 static void PrintPacket (const char* packet, size_t length)
111 {
112 size_t i;
113
114 // Exceptionally, we use MSG_NOPRINT here because if the function is
115 // called, the user probably wants this text to be displayed
116 // whatever the maximum message level is.
117 MsgPrint (MSG_NOPRINT, "\"");
118
119 for (i = 0; i < length; i++)
120 {
121 char c = packet[i];
122 if (c == '\\')
123 MsgPrint (MSG_NOPRINT, "\\\\");
124 else if (c == '\"')
125 MsgPrint (MSG_NOPRINT, "\"");
126 else if (c >= 32 && (qbyte)c <= 127)
127 MsgPrint (MSG_NOPRINT, "%c", c);
128 else
129 MsgPrint (MSG_NOPRINT, "\\x%02X", c);
130 }
131
132 MsgPrint (MSG_NOPRINT, "\" (%u bytes)\n", length);
133 }
134
135
136 /*
137 ====================
138 SysInit
139
140 System dependent initializations
141 ====================
142 */
SysInit(void)143 static qboolean SysInit (void)
144 {
145 #ifdef WIN32
146 WSADATA winsockdata;
147
148 if (WSAStartup (MAKEWORD (1, 1), &winsockdata))
149 {
150 MsgPrint (MSG_ERROR, "ERROR: can't initialize winsocks\n");
151 return qfalse;
152 }
153 #endif
154
155 return qtrue;
156 }
157
158
159 /*
160 ====================
161 UnsecureInit
162
163 System independent initializations, called BEFORE the security initializations.
164 We need this intermediate step because DNS requests may not be able to resolve
165 after the security initializations, due to chroot.
166 ====================
167 */
UnsecureInit(void)168 static qboolean UnsecureInit (void)
169 {
170 // Resolve the address mapping list
171 if (! Sv_ResolveAddressMappings ())
172 return qfalse;
173
174 // Resolve the listen address if one was specified
175 if (listen_name != NULL)
176 {
177 struct hostent* itf;
178
179 itf = gethostbyname (listen_name);
180 if (itf == NULL)
181 {
182 MsgPrint (MSG_ERROR, "ERROR: can't resolve %s\n", listen_name);
183 return qfalse;
184 }
185 if (itf->h_addrtype != AF_INET)
186 {
187 MsgPrint (MSG_ERROR, "ERROR: %s is not an IPv4 address\n",
188 listen_name);
189 return qfalse;
190 }
191
192 memcpy (&listen_addr.s_addr, itf->h_addr,
193 sizeof (listen_addr.s_addr));
194 }
195
196 return qtrue;
197 }
198
199
200 /*
201 ====================
202 SecInit
203
204 Security initializations (system dependent)
205 ====================
206 */
SecInit(void)207 static qboolean SecInit (void)
208 {
209 #ifndef WIN32
210 // Should we run as a daemon?
211 if (daemon_mode && daemon (0, 0))
212 {
213 MsgPrint (MSG_NOPRINT, "ERROR: daemonization failed (%s)\n",
214 strerror (errno));
215 return qfalse;
216 }
217
218 // UNIX allows us to be completely paranoid, so let's go for it
219 if (geteuid () == 0)
220 {
221 struct passwd* pw;
222
223 MsgPrint (MSG_WARNING,
224 "WARNING: running with super-user privileges\n");
225
226 // We must get the account infos before the calls to chroot and chdir
227 pw = getpwnam (low_priv_user);
228 if (pw == NULL)
229 {
230 MsgPrint (MSG_ERROR, "ERROR: can't get user \"%s\" properties\n",
231 low_priv_user);
232 return qfalse;
233 }
234
235 // Chroot ourself
236 MsgPrint (MSG_NORMAL, " - chrooting myself to %s... ", jail_path);
237 if (chroot (jail_path) || chdir ("/"))
238 {
239 MsgPrint (MSG_ERROR, "FAILED (%s)\n", strerror (errno));
240 return qfalse;
241 }
242 MsgPrint (MSG_NORMAL, "succeeded\n");
243
244 // Switch to lower privileges
245 MsgPrint (MSG_NORMAL, " - switching to user \"%s\" privileges... ",
246 low_priv_user);
247 if (setgid (pw->pw_gid) || setuid (pw->pw_uid))
248 {
249 MsgPrint (MSG_ERROR, "FAILED (%s)\n", strerror (errno));
250 return qfalse;
251 }
252 MsgPrint (MSG_NORMAL, "succeeded (UID: %u, GID: %u)\n",
253 pw->pw_uid, pw->pw_gid);
254
255 MsgPrint (MSG_NORMAL, "\n");
256 }
257 #endif
258
259 return qtrue;
260 }
261
262
263 /*
264 ====================
265 ParseCommandLine
266
267 Parse the options passed by the command line
268 ====================
269 */
ParseCommandLine(int argc,const char * argv[])270 static qboolean ParseCommandLine (int argc, const char* argv [])
271 {
272 int ind = 1;
273 unsigned int vlevel = max_msg_level;
274 qboolean valid_options = qtrue;
275
276 while (ind < argc && valid_options)
277 {
278 // If it doesn't even look like an option, why bother?
279 if (argv[ind][0] != '-')
280 valid_options = qfalse;
281
282 else switch (argv[ind][1])
283 {
284 #ifndef WIN32
285 // Daemon mode
286 case 'D':
287 daemon_mode = qtrue;
288 break;
289 #endif
290
291 // Help
292 case 'h':
293 valid_options = qfalse;
294 break;
295
296 // Hash size
297 case 'H':
298 ind++;
299 if (ind < argc)
300 valid_options = Sv_SetHashSize (atoi (argv[ind]));
301 else
302 valid_options = qfalse;
303 break;
304
305 #ifndef WIN32
306 // Jail path
307 case 'j':
308 ind++;
309 if (ind < argc)
310 jail_path = argv[ind];
311 else
312 valid_options = qfalse;
313 break;
314 #endif
315
316 // Listen address
317 case 'l':
318 ind++;
319 if (ind >= argc || argv[ind][0] == '\0')
320 valid_options = qfalse;
321 else
322 listen_name = argv[ind];
323 break;
324
325 // Address mapping
326 case 'm':
327 ind++;
328 if (ind < argc)
329 valid_options = Sv_AddAddressMapping (argv[ind]);
330 else
331 valid_options = qfalse;
332 break;
333
334 // Maximum number of servers
335 case 'n':
336 ind++;
337 if (ind < argc)
338 valid_options = Sv_SetMaxNbServers (atoi (argv[ind]));
339 else
340 valid_options = qfalse;
341 break;
342
343 // Port number
344 case 'p':
345 {
346 unsigned short port_num = 0;
347 ind++;
348 if (ind < argc)
349 port_num = atoi (argv[ind]);
350 if (!port_num)
351 valid_options = qfalse;
352 else
353 master_port = port_num;
354 break;
355 }
356
357 #ifndef WIN32
358 // Low privileges user
359 case 'u':
360 ind++;
361 if (ind < argc)
362 low_priv_user = argv[ind];
363 else
364 valid_options = qfalse;
365 break;
366 #endif
367
368 // Verbose level
369 case 'v':
370 // If a verbose level has been specified
371 if (ind + 1 < argc && argv[ind + 1][0] != '-')
372 {
373 ind++;
374 vlevel = atoi (argv[ind]);
375 if (vlevel > MSG_DEBUG)
376 valid_options = qfalse;
377 }
378 else
379 vlevel = MSG_DEBUG;
380 break;
381
382 default:
383 valid_options = qfalse;
384 }
385
386 ind++;
387 }
388
389 // If the command line is OK, we can set the verbose level now
390 if (valid_options)
391 {
392 #ifndef WIN32
393 // If we run as a daemon, don't bother printing anything
394 if (daemon_mode)
395 max_msg_level = MSG_NOPRINT;
396 else
397 #endif
398 max_msg_level = vlevel;
399 }
400
401 return valid_options;
402 }
403
404
405 /*
406 ====================
407 PrintHelp
408
409 Print the command line syntax and the available options
410 ====================
411 */
PrintHelp(void)412 static void PrintHelp (void)
413 {
414 MsgPrint (MSG_ERROR,
415 "Syntax: dpmaster [options]\n"
416 "Available options are:\n"
417 #ifndef WIN32
418 " -D : run as a daemon\n"
419 #endif
420 " -h : this help\n"
421 " -H <hash_size> : hash size in bits, up to %u (default: %u)\n"
422 #ifndef WIN32
423 " -j <jail_path> : use <jail_path> as chroot path (default: %s)\n"
424 " only available when running with super-user privileges\n"
425 #endif
426 " -l <address> : listen on local address <address>\n"
427 " -m <a1>=<a2> : map address <a1> to <a2> when sending it to clients\n"
428 " addresses can contain a port number (ex: myaddr.net:1234)\n"
429 " -n <max_servers> : maximum number of servers recorded (default: %u)\n"
430 " -p <port_num> : use port <port_num> (default: %u)\n"
431 #ifndef WIN32
432 " -u <user> : use <user> privileges (default: %s)\n"
433 " only available when running with super-user privileges\n"
434 #endif
435 " -v [verbose_lvl] : verbose level, up to %u (default: %u; no value means max)\n"
436 "\n",
437 MAX_HASH_SIZE, DEFAULT_HASH_SIZE,
438 #ifndef WIN32
439 DEFAULT_JAIL_PATH,
440 #endif
441 DEFAULT_MAX_NB_SERVERS,
442 DEFAULT_MASTER_PORT,
443 #ifndef WIN32
444 DEFAULT_LOW_PRIV_USER,
445 #endif
446 MSG_DEBUG, MSG_NORMAL);
447 }
448
449
450 /*
451 ====================
452 SecureInit
453
454 System independent initializations, called AFTER the security initializations
455 ====================
456 */
SecureInit(void)457 static qboolean SecureInit (void)
458 {
459 struct sockaddr_in address;
460
461 // Init the time and the random seed
462 crt_time = time (NULL);
463 srand (crt_time);
464
465 // Initialize the server list and hash table
466 if (!Sv_Init ())
467 return qfalse;
468
469 // Open the socket
470 sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
471 if (sock < 0)
472 {
473 MsgPrint (MSG_ERROR, "ERROR: socket creation failed (%s)\n",
474 strerror (errno));
475 return qfalse;
476 }
477
478 // Bind it to the master port
479 memset (&address, 0, sizeof (address));
480 address.sin_family = AF_INET;
481 if (listen_name != NULL)
482 {
483 MsgPrint (MSG_NORMAL, "Listening on address %s (%s)\n",
484 listen_name, inet_ntoa (listen_addr));
485 address.sin_addr.s_addr = listen_addr.s_addr;
486 }
487 else
488 address.sin_addr.s_addr = htonl (INADDR_ANY);
489 address.sin_port = htons (master_port);
490 if (bind (sock, (struct sockaddr*)&address, sizeof (address)) != 0)
491 {
492 MsgPrint (MSG_ERROR, "ERROR: socket binding failed (%s)\n",
493 strerror (errno));
494 #ifdef WIN32
495 closesocket (sock);
496 #else
497 close (sock);
498 #endif
499 return qfalse;
500 }
501 MsgPrint (MSG_NORMAL, "Listening on UDP port %hu\n",
502 ntohs (address.sin_port));
503
504 return qtrue;
505 }
506
507 static qboolean exitNow = qfalse;
508
509 /*
510 ===============
511 cleanUp
512
513 Clean up
514 ===============
515 */
cleanUp(int signal)516 static void cleanUp( int signal )
517 {
518 MsgPrint( MSG_NORMAL, "Caught signal %d, exiting...\n", signal );
519
520 exitNow = qtrue;
521 }
522
523 /*
524 ====================
525 main
526
527 Main function
528 ====================
529 */
main(int argc,const char * argv[])530 int main (int argc, const char* argv [])
531 {
532 struct sockaddr_in address;
533 socklen_t addrlen;
534 int nb_bytes;
535 char packet [MAX_PACKET_SIZE + 1]; // "+ 1" because we append a '\0'
536 qboolean valid_options;
537 fd_set rfds;
538 struct timeval tv;
539
540
541 signal( SIGINT, cleanUp );
542 signal( SIGTERM, cleanUp );
543
544 // Get the options from the command line
545 valid_options = ParseCommandLine (argc, argv);
546
547 MsgPrint (MSG_NORMAL,
548 "tremmaster (version " VERSION " " __DATE__ " " __TIME__ ")\n" );
549
550 // If there was a mistake in the command line, print the help and exit
551 if (!valid_options)
552 {
553 PrintHelp ();
554 return EXIT_FAILURE;
555 }
556
557 // Initializations
558 if (!SysInit () || !UnsecureInit () || !SecInit () || !SecureInit ())
559 return EXIT_FAILURE;
560 MsgPrint (MSG_NORMAL, "\n");
561
562 // Until the end of times...
563 while( !exitNow )
564 {
565 FD_ZERO( &rfds );
566 FD_SET( sock, &rfds );
567 tv.tv_sec = tv.tv_usec = 0;
568
569 // Check for new data every 100ms
570 if( select( sock + 1, &rfds, NULL, NULL, &tv ) <= 0 )
571 {
572 #ifdef _WIN32
573 Sleep( 100 );
574 #else
575 usleep( 100000 );
576 #endif
577 continue;
578 }
579
580 // Get the next valid message
581 addrlen = sizeof (address);
582 nb_bytes = recvfrom (sock, packet, sizeof (packet) - 1, 0,
583 (struct sockaddr*)&address, &addrlen);
584 if (nb_bytes <= 0)
585 {
586 MsgPrint (MSG_WARNING,
587 "WARNING: \"recvfrom\" returned %d\n", nb_bytes);
588 continue;
589 }
590
591 // If we may have to print something, rebuild the peer address buffer
592 if (max_msg_level != MSG_NOPRINT)
593 snprintf (peer_address, sizeof (peer_address), "%s:%hu",
594 inet_ntoa (address.sin_addr), ntohs (address.sin_port));
595
596 // We print the packet contents if necessary
597 // TODO: print the current time here
598 if (max_msg_level >= MSG_DEBUG)
599 {
600 MsgPrint (MSG_DEBUG, "New packet received from %s: ",
601 peer_address);
602 PrintPacket (packet, nb_bytes);
603 }
604
605 // A few sanity checks
606 if (nb_bytes < MIN_PACKET_SIZE)
607 {
608 MsgPrint (MSG_WARNING,
609 "WARNING: rejected packet from %s (size = %d bytes)\n",
610 peer_address, nb_bytes);
611 continue;
612 }
613 if (*((unsigned int*)packet) != 0xFFFFFFFF)
614 {
615 MsgPrint (MSG_WARNING,
616 "WARNING: rejected packet from %s (invalid header)\n",
617 peer_address);
618 continue;
619 }
620 if (! ntohs (address.sin_port))
621 {
622 MsgPrint (MSG_WARNING,
623 "WARNING: rejected packet from %s (source port = 0)\n",
624 peer_address);
625 continue;
626 }
627
628 // Append a '\0' to make the parsing easier and update the current time
629 packet[nb_bytes] = '\0';
630 crt_time = time (NULL);
631
632 // Call HandleMessage with the remaining contents
633 HandleMessage (packet + 4, nb_bytes - 4, &address);
634 }
635
636 return 0;
637 }
638
639
640 // ---------- Public functions ---------- //
641
642 /*
643 ====================
644 MsgPrint
645
646 Print a message to screen, depending on its verbose level
647 ====================
648 */
MsgPrint(msg_level_t msg_level,const char * format,...)649 int MsgPrint (msg_level_t msg_level, const char* format, ...)
650 {
651 va_list args;
652 int result;
653
654 // If the message level is above the maximum level, don't print it
655 if (msg_level > max_msg_level)
656 return 0;
657
658 va_start (args, format);
659 result = vprintf (format, args);
660 va_end (args);
661
662 fflush (stdout);
663
664 return result;
665 }
666