PennMUSH Community

root/1.8.3/trunk/src/bsd.c

Revision 1167, 126.8 kB (checked in by shawnw, 8 months ago)

Merge devel into trunk for p6 release

Line 
1 /**
2  * \file bsd.c
3  *
4  * \brief Network communication through BSD sockets for PennMUSH.
5  *
6  * While mysocket.c provides low-level functions for working with
7  * sockets, bsd.c focuses on player descriptors, a higher-level
8  * structure that tracks all information associated with a connection,
9  * and through which connection i/o is done.
10  *
11  *
12  */
13
14 #include "copyrite.h"
15 #include "config.h"
16
17 #include <stdio.h>
18 #include <stdarg.h>
19 #ifdef I_SYS_TYPES
20 #include <sys/types.h>
21 #endif
22 #ifdef WIN32
23 #define FD_SETSIZE 256
24 #include <windows.h>
25 #include <winsock.h>
26 #include <io.h>
27 #include <process.h>
28 #define EINTR WSAEINTR
29 #define EWOULDBLOCK WSAEWOULDBLOCK
30 #define MAXHOSTNAMELEN 32
31 #pragma warning( disable : 4761)        /* disable warning re conversion */
32 #else                           /* !WIN32 */
33 #ifdef I_SYS_FILE
34 #include <sys/file.h>
35 #endif
36 #ifdef I_SYS_TIME
37 #include <sys/time.h>
38 #ifdef TIME_WITH_SYS_TIME
39 #include <time.h>
40 #endif
41 #else
42 #include <time.h>
43 #endif
44 #include <sys/ioctl.h>
45 #include <errno.h>
46 #ifdef I_SYS_SOCKET
47 #include <sys/socket.h>
48 #endif
49 #ifdef I_NETINET_IN
50 #include <netinet/in.h>
51 #endif
52 #ifdef I_NETDB
53 #include <netdb.h>
54 #endif
55 #ifdef I_SYS_PARAM
56 #include <sys/param.h>
57 #endif
58 #ifdef I_SYS_STAT
59 #include <sys/stat.h>
60 #endif
61 #endif                          /* !WIN32 */
62 #include <fcntl.h>
63 #include <ctype.h>
64 #include <signal.h>
65 #include <string.h>
66 #include <stdlib.h>
67 #ifdef I_SYS_SELECT
68 #include <sys/select.h>
69 #endif
70 #ifdef HAVE_UNISTD_H
71 #include <unistd.h>
72 #endif
73 #ifdef HAVE_SYS_UIO_H
74 #include <sys/uio.h>
75 #endif
76 #include <limits.h>
77 #ifdef I_FLOATINGPOINT
78 #include <floatingpoint.h>
79 #endif
80 #ifdef HAVE_IEEEFP_H
81 #include <ieeefp.h>
82 #endif
83 #include <locale.h>
84 #include <setjmp.h>
85
86 #include "conf.h"
87
88 #include "externs.h"
89 #include "chunk.h"
90 #include "mushdb.h"
91 #include "dbdefs.h"
92 #include "flags.h"
93 #include "lock.h"
94 #include "help.h"
95 #include "match.h"
96 #include "ansi.h"
97 #include "pueblo.h"
98 #include "parse.h"
99 #include "access.h"
100 #include "command.h"
101 #include "version.h"
102 #include "patches.h"
103 #include "mysocket.h"
104 #include "ident.h"
105
106 #ifndef WIN32
107 #include "wait.h"
108 #ifdef INFO_SLAVE
109 #include "lookup.h"
110 #endif
111 #endif
112
113 #include "strtree.h"
114 #include "log.h"
115 #include "mypcre.h"
116 #ifdef HAS_OPENSSL
117 #include "myssl.h"
118 #endif
119 #include "mymalloc.h"
120 #include "extmail.h"
121 #include "attrib.h"
122 #include "game.h"
123 #include "dbio.h"
124 #include "confmagic.h"
125
126 #ifdef HAS_GETRLIMIT
127 void init_rlimit(void);
128 #endif
129
130
131 /* BSD 4.2 and maybe some others need these defined */
132 #ifndef FD_ZERO
133 /** An fd_set is 4 bytes */
134 #define fd_set int
135 /** Clear an fd_set */
136 #define FD_ZERO(p)       (*p = 0)
137 /** Set a bit in an fd_set */
138 #define FD_SET(n,p)      (*p |= (1<<(n)))
139 /** Clear a bit in an fd_set */
140 #define FD_CLR(n,p)      (*p &= ~(1<<(n)))
141 /** Check a bit in an fd_set */
142 #define FD_ISSET(n,p)    (*p & (1<<(n)))
143 #endif                          /* defines for BSD 4.2 */
144
145 #ifdef HAS_GETRUSAGE
146 void rusage_stats(void);
147 #endif
148 int que_next(void);             /* from cque.c */
149
150 void dispatch(void);            /* from timer.c */
151 dbref email_register_player(const char *name, const char *email, const char *host, const char *ip);     /* from player.c */
152
153 #ifdef SUN_OS
154 static int extrafd;
155 #endif
156 int shutdown_flag = 0;          /**< Is it time to shut down? */
157 void chat_player_announce(dbref player, char *msg, int ungag);
158
159 static int login_number = 0;
160 static int under_limit = 1;
161
162 char cf_motd_msg[BUFFER_LEN] = { '\0' }; /**< The message of the day */
163 char cf_wizmotd_msg[BUFFER_LEN] = { '\0' };      /**< The wizard motd */
164 char cf_downmotd_msg[BUFFER_LEN] = { '\0' };     /**< The down message */
165 char cf_fullmotd_msg[BUFFER_LEN] = { '\0' };     /**< The 'mush full' message */
166 static char poll_msg[DOING_LEN] = { '\0' };
167 char confname[BUFFER_LEN] = { '\0' };    /**< Name of the config file */
168 char errlog[BUFFER_LEN] = { '\0' };      /**< Name of the error log file */
169
170 /** Is this descriptor connected to a telnet-compatible terminal? */
171 #define TELNET_ABLE(d) ((d)->conn_flags & (CONN_TELNET | CONN_TELNET_QUERY))
172
173
174 /* When the mush gets a new connection, it tries sending a telnet
175  * option negotiation code for setting client-side line-editing mode
176  * to it. If it gets a reply, a flag in the descriptor struct is
177  * turned on indicated telnet-awareness.
178  *
179  * If the reply indicates that the client supports linemode, further
180  * instructions as to what linemode options are to be used is sent.
181  * Those options: Client-side line editing, and expanding literal
182  * client-side-entered tabs into spaces.
183  *
184  * Option negotation requests sent by the client are processed,
185  * with the only one we confirm rather than refuse outright being
186  * suppress-go-ahead, since a number of telnet clients try it.
187  *
188  * The character 255 is the telnet option escape character, so when it
189  * is sent to a telnet-aware client by itself (Since it's also often y-umlaut)
190  * it must be doubled to escape it for the client. This is done automatically,
191  * and is the original purpose of adding telnet option support.
192  */
193
194 /* Telnet codes */
195 #define IAC 255                 /**< interpret as command: */
196 #define NOP 241                 /**< no operation */
197 #define AYT 246                 /**< are you there? */
198 #define DONT 254                /**< you are not to use option */
199 #define DO      253             /**< please, you use option */
200 #define WONT 252                /**< I won't use option */
201 #define WILL    251             /**< I will use option */
202 #define SB      250             /**< interpret as subnegotiation */
203 #define SE      240             /**< end sub negotiation */
204 #define TN_SGA 3                /**< Suppress go-ahead */
205 #define TN_LINEMODE 34          /**< Line mode */
206 #define TN_NAWS 31              /**< Negotiate About Window Size */
207 #define TN_TTYPE 24             /**< Ask for termial type information */
208 static void test_telnet(DESC *d);
209 static void setup_telnet(DESC *d);
210 static int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend);
211
212 /** Iterate through a list of descriptors, and do something with those
213  * that are connected.
214  */
215 #define DESC_ITER_CONN(d) \
216         for(d = descriptor_list;(d);d=(d)->next) \
217           if((d)->connected)
218
219 /** Is a descriptor hidden? */
220 #define Hidden(d)        ((d->hide == 1) && Can_Hide(d->player))
221
222 static const char *create_fail =
223   "Either there is already a player with that name, or that name is illegal.";
224 static const char *password_fail = "The password is invalid (or missing).";
225 static const char *register_fail =
226   "Unable to register that player with that email address.";
227 static const char *register_success =
228   "Registration successful! You will receive your password by email.";
229 static const char *shutdown_message = "Going down - Bye";
230 #ifdef HAS_OPENSSL
231 static const char *ssl_shutdown_message =
232   "GAME: SSL connections must be dropped, sorry.";
233 #endif
234 static const char *asterisk_line =
235   "**********************************************************************";
236 /** Where we save the descriptor info across reboots. */
237 #define REBOOTFILE              "reboot.db"
238
239 #if 0
240 /* For translation */
241 static void dummy_msgs(void);
242 static void
243 dummy_msgs()
244 {
245   char *temp;
246   temp = T("Either that player does not exist, or has a different password.");
247   temp =
248     T
249     ("Either there is already a player with that name, or that name is illegal.");
250   temp = T("The password is invalid (or missing).");
251   temp = T("Unable to register that player with that email address.");
252   temp = T("Registration successful! You will receive your password by email.");
253   temp = T("Going down - Bye");
254   temp = T("GAME: SSL connections must be dropped, sorry.");
255 }
256
257 #endif
258
259 DESC *descriptor_list = NULL;   /**< The linked list of descriptors */
260
261 static int sock;
262 #ifdef HAS_OPENSSL
263 static int sslsock = 0;
264 SSL *ssl_master_socket = NULL;  /**< Master SSL socket for ssl port */
265 #endif
266 static int ndescriptors = 0;
267 #ifdef WIN32
268 static WSADATA wsadata;
269 #endif
270 int restarting = 0;     /**< Are we restarting the server after a reboot? */
271 int maxd = 0;
272
273 extern const unsigned char *tables;
274
275 sig_atomic_t signal_shutdown_flag = 0;  /**< Have we caught a shutdown signal? */
276 sig_atomic_t signal_dump_flag = 0;      /**< Have we caught a dump signal? */
277
278 #ifndef BOOLEXP_DEBUGGING
279 #ifdef WIN32SERVICES
280 void shutdown_checkpoint(void);
281 int mainthread(int argc, char **argv);
282 #else
283 int main(int argc, char **argv);
284 #endif
285 #endif
286 void set_signals(void);
287 static struct timeval *timeval_sub(struct timeval *now, struct timeval *then);
288 #ifdef WIN32
289 /** Windows doesn't have gettimeofday(), so we implement it here */
290 #define our_gettimeofday(now) win_gettimeofday((now))
291 static void win_gettimeofday(struct timeval *now);
292 #else
293 /** A wrapper for gettimeofday() in case the system doesn't have it */
294 #define our_gettimeofday(now) gettimeofday((now), (struct timezone *)NULL)
295 #endif
296 static long int msec_diff(struct timeval *now, struct timeval *then);
297 static struct timeval *msec_add(struct timeval *t, int x);
298 static void update_quotas(struct timeval *last, struct timeval *current);
299
300 int how_many_fds(void);
301 static void shovechars(Port_t port, Port_t sslport);
302 static int test_connection(int newsock);
303 #ifndef INFO_SLAVE
304 static DESC *new_connection(int oldsock, int *result, int use_ssl);
305 #endif
306
307 static void clearstrings(DESC *d);
308
309 /** A block of cached text. */
310 typedef struct fblock {
311   unsigned char *buff;    /**< Pointer to the block as a string */
312   size_t len;             /**< Length of buff */
313 } FBLOCK;
314
315 /** The complete collection of cached text files. */
316 struct fcache_entries {
317   FBLOCK connect_fcache[2];     /**< connect.txt and connect.html */
318   FBLOCK motd_fcache[2];        /**< motd.txt and motd.html */
319   FBLOCK wizmotd_fcache[2];     /**< wizmotd.txt and wizmotd.html */
320   FBLOCK newuser_fcache[2];     /**< newuser.txt and newuser.html */
321   FBLOCK register_fcache[2];    /**< register.txt and register.html */
322   FBLOCK quit_fcache[2];        /**< quit.txt and quit.html */
323   FBLOCK down_fcache[2];        /**< down.txt and down.html */
324   FBLOCK full_fcache[2];        /**< full.txt and full.html */
325   FBLOCK guest_fcache[2];       /**< guest.txt and guest.html */
326 };
327
328 static struct fcache_entries fcache;
329 static void fcache_dump(DESC *d, FBLOCK fp[2], const unsigned char *prefix);
330 static int fcache_read(FBLOCK *cp, const char *filename);
331 static void logout_sock(DESC *d);
332 static void shutdownsock(DESC *d);
333 DESC *initializesock(int s, char *addr, char *ip, int use_ssl);
334 int process_output(DESC *d);
335 /* Notify.c */
336 extern void free_text_block(struct text_block *t);
337 extern void add_to_queue(struct text_queue *q, const unsigned char *b, int n);
338 extern int queue_write(DESC *d, const unsigned char *b, int n);
339 extern int queue_eol(DESC *d);
340 extern int queue_newwrite(DESC *d, const unsigned char *b, int n);
341 extern int queue_string(DESC *d, const char *s);
342 extern int queue_string_eol(DESC *d, const char *s);
343 extern void freeqs(DESC *d);
344 static void welcome_user(DESC *d);
345 static void dump_info(DESC *call_by);
346 static void save_command(DESC *d, const unsigned char *command);
347 static int process_input(DESC *d, int output_ready);
348 static void process_input_helper(DESC *d, char *tbuf1, int got);
349 static void set_userstring(unsigned char **userstring, const char *command);
350 static void process_commands(void);
351 static int do_command(DESC *d, char *command);
352 static void parse_puebloclient(DESC *d, char *command);
353 static int dump_messages(DESC *d, dbref player, int new);
354 static int check_connect(DESC *d, const char *msg);
355 static void parse_connect(const char *msg, char *command, char *user,
356                           char *pass);
357 static void close_sockets(void);
358 dbref find_player_by_desc(int port);
359 static DESC *lookup_desc(dbref executor, const char *name);
360 void NORETURN bailout(int sig);
361 void WIN32_CDECL signal_shutdown(int sig);
362 void WIN32_CDECL signal_dump(int sig);
363 void reaper(int sig);
364 #ifndef WIN32
365 sig_atomic_t dump_error = 0;
366 WAIT_TYPE dump_status = 0;
367 #ifdef INFO_SLAVE
368 sig_atomic_t slave_error = 0;
369 #endif
370 #endif
371 extern pid_t forked_dump_pid;   /**< Process id of forking dump process */
372 static void dump_users(DESC *call_by, char *match, int doing);
373 static const char *time_format_1(time_t dt);
374 static const char *time_format_2(time_t dt);
375 static void announce_connect(dbref player, int isnew, int num);
376 static void announce_disconnect(DESC *saved);
377 void inactivity_check(void);
378 void reopen_logs(void);
379 void load_reboot_db(void);
380
381 static bool in_suid_root_mode = 0;
382 static char *pidfile = NULL;
383 static char **saved_argv = NULL;
384
385 #ifndef BOOLEXP_DEBUGGING
386 #ifdef WIN32SERVICES
387 /* Under WIN32, MUSH is a "service", so we just start a thread here.
388  * The real "main" is in win32/services.c
389  */
390 int
391 mainthread(int argc, char **argv)
392 #else
393 /** The main function.
394  * \param argc number of arguments.
395  * \param argv vector of arguments.
396  * \return exit code.
397  */
398 int
399 main(int argc, char **argv)
400 #endif                          /* WIN32SERVICES */
401 {
402   FILE *newerr;
403   bool detach_session = 1;
404
405 /* disallow running as root on unix.
406  * This is done as early as possible, before translation is initialized.
407  * Hence, no T()s around messages.
408  */
409 #ifndef WIN32
410 #ifdef HAVE_GETUID
411   if (getuid() == 0) {
412     fputs("Please run the server as another user.\n", stderr);
413     fputs("PennMUSH will not run as root as a security measure.\n", stderr);
414     return EXIT_FAILURE;
415   }
416   /* Add suid-root checks here. */
417 #endif
418 #ifdef HAVE_GETEUID
419   if (geteuid() == 0) {
420     fprintf(stderr, "The  %s binary is set suid and owned by root.\n", argv[0]);
421 #ifdef HAVE_SETEUID
422     fprintf(stderr, "Changing effective user to %d.\n", getuid());
423     seteuid(getuid());
424     in_suid_root_mode = 1;
425 #endif
426   }
427 #endif                          /* HAVE_GETEUID */
428 #endif                          /* !WIN32 */
429
430   /* read the configuration file */
431   if (argc < 2) {
432     fprintf(stderr,
433             "WARNING: Called without a config file argument. Assuming mush.cnf\n");
434     strcpy(confname, "mush.cnf");
435   } else {
436     int n;
437     for (n = 1; n < argc; n++) {
438       if (argv[n][0] == '-') {
439         if (strcmp(argv[n], "--no-session") == 0)
440           detach_session = 0;
441         else if (strncmp(argv[n], "--pid-file", 10) == 0) {
442           char *eq;
443           if ((eq = strchr(argv[n], '=')))
444             pidfile = eq + 1;
445           else {
446             if (n + 1 >= argc) {
447               fprintf(stderr, "%s: --pid-file needs a filename.\n", argv[0]);
448               return EXIT_FAILURE;
449             }
450             pidfile = argv[n + 1];
451             n++;
452           }
453         } else
454           fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[n]);
455       } else {
456         mush_strncpy(confname, argv[n], BUFFER_LEN);
457         break;
458       }
459     }
460   }
461
462
463 #ifdef HAVE_FORK
464   /* Fork off and detach from controlling terminal. */
465   if (detach_session) {
466     pid_t child;
467
468     child = fork();
469     if (child < 0) {
470       /* Print a warning and continue */
471       penn_perror("fork");
472     } else if (child > 0) {
473       /* Parent process of a successful fork() */
474       return EXIT_SUCCESS;
475     } else {
476       /* Child process */
477       if (new_process_session() < 0)
478         penn_perror("Couldn't create a new process session");
479     }
480   }
481 #endif
482
483 #ifdef HAVE_GETPID
484   if (pidfile) {
485     FILE *f;
486     if (!(f = fopen(pidfile, "w"))) {
487       fprintf(stderr, "%s: Unable to write to pidfile '%s'\n", argv[0],
488               pidfile);
489       return EXIT_FAILURE;
490     }
491     fprintf(f, "%d\n", getpid());
492     fclose(f);
493   }
494 #endif
495
496   saved_argv = argv;
497
498 #ifdef WIN32
499   {
500     unsigned short wVersionRequested = MAKEWORD(1, 1);
501     int err;
502
503     /* Need to include library: wsock32.lib for Windows Sockets */
504     err = WSAStartup(wVersionRequested, &wsadata);
505     if (err) {
506       printf(T("Error %i on WSAStartup\n"), err);
507       exit(1);
508     }
509   }
510 #endif                          /* WIN32 */
511
512 #ifdef HAS_GETRLIMIT
513   init_rlimit();                /* unlimit file descriptors */
514 #endif
515
516   /* These are BSDisms to fix floating point exceptions */
517 #ifdef HAVE_FPSETROUND
518   fpsetround(FP_RN);
519 #endif
520 #ifdef HAVE_FPSETMASK
521   fpsetmask(0L);
522 #endif
523
524   time(&mudtime);
525
526   options.mem_check = 1;
527
528   /* If we have setlocale, call it to set locale info
529    * from environment variables
530    */
531 #ifdef HAS_SETLOCALE
532   {
533     char *loc;
534     if ((loc = setlocale(LC_CTYPE, "")) == NULL)
535       do_rawlog(LT_ERR, "Failed to set ctype locale from environment.");
536     else
537       do_rawlog(LT_ERR, "Setting ctype locale to %s", loc);
538     if ((loc = setlocale(LC_TIME, "")) == NULL)
539       do_rawlog(LT_ERR, "Failed to set time locale from environment.");
540     else
541       do_rawlog(LT_ERR, "Setting time locale to %s", loc);
542 #ifdef LC_MESSAGES
543     if ((loc = setlocale(LC_MESSAGES, "")) == NULL)
544       do_rawlog(LT_ERR, "Failed to set messages locale from environment.");
545     else
546       do_rawlog(LT_ERR, "Setting messages locale to %s", loc);
547 #else
548     do_rawlog(LT_ERR, "No support for message locale.");
549 #endif
550     if ((loc = setlocale(LC_COLLATE, "")) == NULL)
551       do_rawlog(LT_ERR, "Failed to set collate locale from environment.");
552     else
553       do_rawlog(LT_ERR, "Setting collate locale to %s", loc);
554   }
555 #endif
556 #ifdef HAS_TEXTDOMAIN
557   textdomain("pennmush");
558 #endif
559 #ifdef HAS_BINDTEXTDOMAIN
560   bindtextdomain("pennmush", "../po");
561 #endif
562
563   /* Build the locale-dependant tables used by PCRE */
564   tables = pcre_maketables();
565
566   init_game_config(confname);
567
568   /* save a file descriptor */
569   reserve_fd();
570 #ifdef SUN_OS
571   extrafd = open("/dev/null", O_RDWR);
572 #endif
573
574   /* decide if we're in @shutdown/reboot */
575   restarting = 0;
576   newerr = fopen(REBOOTFILE, "r");
577   if (newerr) {
578     restarting = 1;
579     fclose(newerr);
580   }
581
582   if (init_game_dbs() < 0) {
583     do_rawlog(LT_ERR, T("ERROR: Couldn't load databases! Exiting."));
584     exit(2);
585   }
586
587   init_game_postdb(confname);
588
589   globals.database_loaded = 1;
590
591   set_signals();
592
593 #ifdef INFO_SLAVE
594   init_info_slave();
595 #endif
596
597   /* go do it */
598 #ifdef CSRI
599 #ifdef CSRI_DEBUG
600   mal_verify(1);