1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (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.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  play.c: Playing the game
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2007-2017
31  *     Ron Koenderink, 2007-2009
32  *     Martin Haukeli, 2015
33  */
34 
35 #include <config.h>
36 
37 #include <assert.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #ifdef _WIN32
44 #include <process.h>
45 #include <sys/socket.h>
46 #else
47 #include <sys/select.h>
48 #endif
49 #include <unistd.h>
50 #include "linebuf.h"
51 #include "misc.h"
52 #include "proto.h"
53 #include "ringbuf.h"
54 #include "secure.h"
55 
56 #ifdef HAVE_LIBREADLINE
57 #include <readline/readline.h>
58 #include <readline/history.h>
59 #endif
60 
61 #define EOF_COOKIE "ctld\n"
62 #define INTR_COOKIE "aborted\n"
63 
64 /*
65  * Player input file descriptor
66  * 0 while reading interactive input
67  * >0 while reading a batch file
68  * <0 during error handling
69  */
70 static int input_fd;
71 
72 static volatile sig_atomic_t send_intr; /* need to send INTR_COOKIE */
73 
74 #ifdef _WIN32
75 static CRITICAL_SECTION signal_critical_section;
76 static LPCRITICAL_SECTION signal_critical_section_ptr = NULL;
77 
78 static unsigned char bounce_buf[RING_SIZE];
79 /*
80  * Set bounce_empty to indicate bounce_buf is available for the stdin thread
81  * to use.
82  */
83 static HANDLE bounce_empty;
84 /*
85  * Set bounce_full to indicate bounce_buf is contains data from the
86  * stdin thread and is available for recv_input
87  */
88 static HANDLE bounce_full;
89 /* Ctrl-C (SIGINT) was detected, generate EINTR for the w32_select() */
90 static HANDLE ctrl_c_event;
91 static int bounce_status, bounce_error;
92 
93 struct sigaction {
94     int sa_flags;
95     void (*sa_handler)(int sig);
96 };
97 
98 #define SIGPIPE -1
99 static void (*ctrl_handler)(int sig) = { SIG_DFL };
100 
101 /*
102  * Ctrl-C handler for emulating the SIGINT in WIN32
103  */
104 static BOOL WINAPI
w32_signal_handler(DWORD ctrl_type)105 w32_signal_handler(DWORD ctrl_type)
106 {
107     if (ctrl_type == CTRL_C_EVENT) {
108 	EnterCriticalSection(signal_critical_section_ptr);
109 	if (ctrl_handler != SIG_DFL) {
110 	    ctrl_handler(SIGINT);
111 	    LeaveCriticalSection(signal_critical_section_ptr);
112 	    SetEvent(ctrl_c_event);
113 	    return TRUE;
114 	} else
115 	    LeaveCriticalSection(signal_critical_section_ptr);
116     }
117     return FALSE;
118 }
119 
120 /*
121  * WIN32 equivalent for sigaction supports the following:
122  * set handler for SIGINT using WIN32 Ctrl-C handler
123  * reset handler SIGINT to SIG_DFL
124  * ignore SIGPIPE
125  */
126 static int
sigaction(int signal,struct sigaction * action,struct sigaction * oaction)127 sigaction(int signal, struct sigaction *action, struct sigaction *oaction)
128 {
129     assert(!oaction);
130     assert(action);
131 
132     if (signal == SIGPIPE)
133 	assert(action->sa_handler == SIG_IGN);
134     else {
135 	assert(signal == SIGINT && action->sa_handler != SIG_IGN);
136 	if (ctrl_handler == action->sa_handler)
137 	    return 0;
138 	if (signal_critical_section_ptr == NULL) {
139 	    signal_critical_section_ptr = &signal_critical_section;
140 	    InitializeCriticalSection(signal_critical_section_ptr);
141 	}
142 	EnterCriticalSection(signal_critical_section_ptr);
143 	if (!SetConsoleCtrlHandler(w32_signal_handler,
144 				   action->sa_handler != SIG_DFL)) {
145 	    errno = GetLastError();
146 	    LeaveCriticalSection(signal_critical_section_ptr);
147 	    return -1;
148 	}
149 	ctrl_handler = action->sa_handler;
150 	LeaveCriticalSection(signal_critical_section_ptr);
151     }
152     return 0;
153 }
154 
155 /*
156  * Read the stdin in WIN32 environment
157  * WIN32 does not support select type function on console input
158  * so the client uses a separate thread to read input
159  */
160 static void
stdin_read_thread(void * dummy)161 stdin_read_thread(void *dummy)
162 {
163     for (;;) {
164 	switch (WaitForSingleObject(bounce_empty, INFINITE)) {
165 	case WAIT_FAILED:
166 	    bounce_status = -1;
167 	    bounce_error = GetLastError();
168 	    break;
169 	case WAIT_OBJECT_0:
170 	    bounce_status = _read(0, bounce_buf, sizeof(bounce_buf));
171 	    bounce_error = errno;
172 	    break;
173 	case WAIT_ABANDONED:
174 	    return;
175 	default:
176 	    assert(0);
177 	}
178 	SetEvent(bounce_full);
179     }
180 }
181 
182 /*
183  * Initialize and start the stdin reading thread for WIN32
184  */
185 static void
sysdep_stdin_init(void)186 sysdep_stdin_init(void)
187 {
188     bounce_empty = CreateEvent(NULL, FALSE, TRUE, NULL);
189     bounce_full = CreateEvent(NULL, TRUE, FALSE, NULL);
190     ctrl_c_event = CreateEvent(NULL, FALSE, FALSE, NULL);
191     _beginthread(stdin_read_thread, 0, NULL);
192 }
193 
194 /*
195  * This function uses to WaitForMultipleObjects to wait for both
196  * stdin and socket reading or writing.
197  * Stdin is treated special in WIN32.  Waiting for stdin is done
198  * via a bounce_full event which is set in the stdin thread.
199  * Execute command file reading is done via handle.
200  * WaitForMultipleObjects will only respond with one object,
201  * so an additonal select is also done to determine
202  * which individual events are active.
203  */
204 static int
w32_select(int nfds,fd_set * rdfd,fd_set * wrfd,fd_set * errfd,struct timeval * time)205 w32_select(int nfds, fd_set *rdfd, fd_set *wrfd, fd_set *errfd,
206 	   struct timeval *time)
207 {
208     HANDLE handles[3];
209     SOCKET sock;
210     int inp, sockfd, result, s_result, num_handles;
211     struct timeval tv_time = {0, 0};
212     fd_set rdsock, wrsock;
213 
214     switch (rdfd->fd_count) {
215     case 1:
216 	inp = -1;
217 	sockfd = rdfd->fd_array[0];
218 	break;
219     case 2:
220 	inp = rdfd->fd_array[0];
221 	sockfd = rdfd->fd_array[1];
222 	break;
223     default:
224 	assert(0);
225     }
226     sock = w32_fd2socket(sockfd);
227 
228     assert(wrfd->fd_count == 0
229 	   || (wrfd->fd_count == 1 && wrfd->fd_array[0] == (SOCKET)sockfd));
230     assert(inp < 0 || inp == input_fd);
231 
232     num_handles = 0;
233     handles[num_handles++] = ctrl_c_event;
234     if (inp >= 0)
235 	handles[num_handles++]
236 	    = inp ? (HANDLE)_get_osfhandle(inp) : bounce_full;
237     /* always wait on the socket */
238     handles[num_handles++] = WSACreateEvent();
239 
240     if (wrfd->fd_count > 0)
241 	WSAEventSelect(sock, handles[num_handles - 1],
242 		       FD_READ | FD_WRITE | FD_CLOSE);
243     else
244 	WSAEventSelect(sock, handles[num_handles - 1],
245 		       FD_READ | FD_CLOSE);
246 
247     result = WaitForMultipleObjects(num_handles, handles, 0, INFINITE);
248     if (result < 0) {
249 	errno = GetLastError();
250 	WSACloseEvent(handles[num_handles - 1]);
251 	return -1;
252     }
253     WSACloseEvent(handles[num_handles - 1]);
254 
255     if (result == WAIT_OBJECT_0) {
256 	errno = EINTR;
257 	return -1;
258     }
259 
260     FD_ZERO(&rdsock);
261     FD_ZERO(&wrsock);
262     FD_SET(sock, &rdsock);
263     if (wrfd->fd_count)
264 	FD_SET(sock, &wrsock);
265     s_result = select(sock + 1, &rdsock, &wrsock, NULL, &tv_time);
266 
267     if (s_result < 0) {
268 	w32_set_winsock_errno();
269 	return s_result;
270     }
271 
272     if (!FD_ISSET(sock, &rdsock))
273 	FD_CLR((SOCKET)sockfd, rdfd);
274     if (!FD_ISSET(sock, &wrsock))
275 	FD_CLR((SOCKET)sockfd, wrfd);
276     if (inp >= 0 && result == WAIT_OBJECT_0 + 1)
277 	s_result++;
278     else
279 	FD_CLR((SOCKET)inp, rdfd);
280 
281     return s_result;
282 }
283 
284 /*
285  * Read input from the user either stdin or from file.
286  * For stdin, read from bounce_buf which filled by the stdin thread
287  * otherwise use the regular ring_from_file.
288  */
289 static int
w32_ring_from_file_or_bounce_buf(struct ring * r,int fd)290 w32_ring_from_file_or_bounce_buf(struct ring *r, int fd)
291 {
292     int i, res;
293 
294     if (fd)
295 	return ring_from_file(r, fd);
296 
297     if (bounce_status < 0) {
298 	errno = bounce_error;
299 	res = bounce_status;
300     } else {
301 	for (i = 0; i < bounce_status; i++) {
302 	    if (ring_putc(r, bounce_buf[i]) == EOF) {
303 		/* more work to do, hold on to bounce_buf */
304 		memmove(bounce_buf, bounce_buf + i, bounce_status - i);
305 		bounce_status -= i;
306 		return i;
307 	    }
308 	}
309 	res = i;
310     }
311 
312     ResetEvent(bounce_full);
313     SetEvent(bounce_empty);
314     return res;
315 }
316 #define ring_from_file w32_ring_from_file_or_bounce_buf
317 #define select(nfds, rd, wr, error, time) \
318 	w32_select((nfds), (rd), (wr), (error), (time))
319 #define sigemptyset(mask) ((void)0)
320 #else
321 #define sysdep_stdin_init() ((void)0)
322 #endif
323 
324 /*
325  * Receive and process server output from @sock.
326  * Return number of characters received on success, -1 on error.
327  */
328 static int
recv_output(int sock)329 recv_output(int sock)
330 {
331     /*
332      * Read a chunk of server output and feed its characters into a
333      * simple state machine.
334      * Initial state is SCANNING_ID.
335      * In state SCANNING_ID, buffer the character.  If it's a space,
336      * decode the ID that has been buffered, and enter state BUFFERING
337      * or COPYING depending on its value.
338      * In state BUFFERING, buffer the character.  If it's newline,
339      * pass ID and buffered text to servercmd(), then enter state
340      * SCANNING_ID.
341      * In state COPYING, pass the character to outch().  If it's
342      * newline, enter state SCANNING_ID.
343      */
344     static enum {
345 	SCANNING_ID, BUFFERING, COPYING
346     } state = SCANNING_ID;
347     static int id;
348     static struct lbuf lbuf;
349     char buf[4096];
350     ssize_t n;
351     int i, ch, len, fd;
352     char *line;
353 
354     n = read(sock, buf, sizeof(buf));
355     if (n < 0)
356 	return -1;
357 
358     for (i = 0; i < n; i++) {
359 	ch = buf[i];
360 	switch (state) {
361 	case SCANNING_ID:
362 	    if (ch == '\n') {
363 		/* FIXME gripe unexpected! */
364 		lbuf_init(&lbuf);
365 		break;
366 	    }
367 	    lbuf_putc(&lbuf, ch);
368 	    if (ch != ' ')
369 		break;
370 	    line = lbuf_line(&lbuf);
371 	    id = parseid(line);
372 	    lbuf_init(&lbuf);
373 
374 	    switch (id) {
375 	    case C_PROMPT:
376 	    case C_FLUSH:
377 	    case C_EXECUTE:
378 	    case C_EXIT:
379 	    case C_FLASH:
380 	    case C_INFORM:
381 	    case C_PIPE:
382 	    case C_REDIR:
383 		state = BUFFERING;
384 		break;
385 	    default:
386 		/* unknown or unexpected ID, treat like C_DATA */
387 	    case C_DATA:
388 		state = COPYING;
389 		break;
390 	    }
391 	    break;
392 
393 	case BUFFERING:
394 	    len = lbuf_putc(&lbuf, ch);
395 	    if (len) {
396 		line = lbuf_line(&lbuf);
397 		fd = servercmd(id, line, len);
398 		if (fd < 0) {
399 		    /* failed execute */
400 		    if (input_fd)
401 			close(input_fd);
402 		    input_fd = 0;
403 		    send_intr = 1;
404 		} else if (fd > 0) {
405 		    /* successful execute, switch to batch file */
406 		    assert(!input_fd);
407 		    input_fd = fd;
408 		}
409 		lbuf_init(&lbuf);
410 		state = SCANNING_ID;
411 	    }
412 	    break;
413 
414 	case COPYING:
415 	    outch(ch);
416 	    if (ch == '\n')
417 		state = SCANNING_ID;
418 	}
419     }
420 
421     return n;
422 }
423 
424 #ifdef HAVE_LIBREADLINE
425 static int use_readline;
426 static char *input_from_rl;
427 static int has_rl_input;
428 
429 static void
input_handler(char * line)430 input_handler(char *line)
431 {
432     input_from_rl = line;
433     has_rl_input = 1;
434     if (line && *line)
435 	add_history(line);
436 }
437 
438 static int
ring_from_rl(struct ring * inbuf)439 ring_from_rl(struct ring *inbuf)
440 {
441     size_t len;
442     int n;
443 
444     assert(has_rl_input && input_from_rl);
445 
446     len = strlen(input_from_rl);
447     n = ring_space(inbuf);
448     assert(n);
449 
450     if (len >= (size_t)n) {
451 	ring_putm(inbuf, input_from_rl, n);
452 	memmove(input_from_rl, input_from_rl + n, len - n + 1);
453     } else {
454 	ring_putm(inbuf, input_from_rl, len);
455 	ring_putc(inbuf, '\n');
456 	free(input_from_rl);
457 	has_rl_input = 0;
458 	n = len + 1;
459     }
460 
461     return n;
462 }
463 #endif /* HAVE_LIBREADLINE */
464 
465 /*
466  * Receive player input from @fd into @inbuf.
467  * Return 1 on receipt of input, zero on EOF, -1 on error.
468  */
469 static int
recv_input(int fd,struct ring * inbuf)470 recv_input(int fd, struct ring *inbuf)
471 {
472     int n;
473     int res = 1;
474 
475 #ifdef HAVE_LIBREADLINE
476     if (fd == 0 && use_readline) {
477 	if (!has_rl_input)
478 	    rl_callback_read_char();
479 	if (!has_rl_input)
480 	    return 1;
481 	if (input_from_rl) {
482 	    n = ring_from_rl(inbuf);
483 	} else
484 	    n = 0;
485     } else
486 #endif
487 	n = ring_from_file(inbuf, fd);
488     if (n < 0)
489 	return -1;
490     if (n == 0) {
491 	/*
492 	 * Can't put EOF cookie into @inbuf here, it may not fit.
493 	 * Leave it to caller.
494 	 */
495 	res = 0;
496     }
497 
498     return res;
499 }
500 
501 static int
send_input(int fd,struct ring * inbuf)502 send_input(int fd, struct ring *inbuf)
503 {
504     struct iovec iov[2];
505     int cnt, i, ch;
506     ssize_t res;
507 
508     cnt = ring_to_iovec(inbuf, iov);
509     res = writev(fd, iov, cnt);
510     if (res < 0)
511 	return res;
512 
513     /* Copy input to @auxfp etc. */
514     for (i = 0; i < res; i++) {
515 	ch = ring_getc(inbuf);
516 	assert(ch != EOF);
517 	if (ch != '\r')
518 	    save_input(ch);
519 	if (auxfp)
520 	    putc(ch, auxfp);
521     }
522 
523 #ifdef HAVE_LIBREADLINE
524     if (fd == 0 && use_readline && has_rl_input && input_from_rl)
525 	ring_from_rl(inbuf);
526 #endif
527 
528     return res;
529 }
530 
531 static void
intr(int sig)532 intr(int sig)
533 {
534     send_intr = 1;
535 }
536 
537 /*
538  * Play on @sock.
539  * @history_file is the name of the history file, or null.
540  * The session must be in the playing phase.
541  * Return 0 when the session ended, -1 on error.
542  */
543 int
play(int sock,char * history_file)544 play(int sock, char *history_file)
545 {
546     /*
547      * Player input flows from @input_fd through recv_input() into
548      * ring buffer @inbuf, which drains into @sock.  This must not
549      * block.  Server output flows from @sock into recv_output().
550      * Reading @sock must not block.
551      */
552     struct sigaction sa;
553     struct ring inbuf;		/* input buffer, draining to SOCK */
554     int eof_fd0;		/* read fd 0 hit EOF? */
555     int partial_line_sent;	/* partial input line sent? */
556     int send_eof;		/* need to send EOF_COOKIE */
557     fd_set rdfd, wrfd;
558     int n;
559     int ret = -1;
560 
561     sa.sa_flags = 0;
562     sigemptyset(&sa.sa_mask);
563     sa.sa_handler = intr;
564     sigaction(SIGINT, &sa, NULL);
565     sa.sa_handler = SIG_IGN;
566     sigaction(SIGPIPE, &sa, NULL);
567 #ifdef HAVE_LIBREADLINE
568     if (isatty(0)) {
569 	use_readline = 1;
570 	rl_already_prompted = 1;
571 	rl_readline_name = "Empire";
572 	if (history_file)
573 	    read_history(history_file);
574 	rl_bind_key('\t', rl_insert);  /* Disable tab completion */
575 	rl_callback_handler_install("", input_handler);
576     }
577 #endif /* HAVE_LIBREADLINE */
578 
579     ring_init(&inbuf);
580     eof_fd0 = partial_line_sent = send_eof = send_intr = 0;
581     input_fd = 0;
582     sysdep_stdin_init();
583 
584     for (;;) {
585 	FD_ZERO(&rdfd);
586 	FD_ZERO(&wrfd);
587 
588 	/*
589 	 * Want to read player input only when we don't need to send
590 	 * cookies, haven't reached EOF on fd 0, and @inbuf can accept
591 	 * some.
592 	 */
593 	if (!send_intr && !send_eof && (input_fd || !eof_fd0)
594 	    && ring_space(&inbuf))
595 	    FD_SET(input_fd, &rdfd);
596 	/* Want to send player input only when we have something */
597 	if (send_intr || send_eof || ring_len(&inbuf))
598 	    FD_SET(sock, &wrfd);
599 	/* Always want to read server output */
600 	FD_SET(sock, &rdfd);
601 
602 	n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL);
603 	if (n < 0) {
604 	    if (errno != EINTR) {
605 		perror("select");
606 		break;
607 	    }
608 	}
609 
610 	if ((send_eof || send_intr) && partial_line_sent
611 	    && ring_putc(&inbuf, '\n') != EOF)
612 	    partial_line_sent = 0;
613 	if (send_eof && !partial_line_sent
614 	    && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0)
615 	    send_eof = 0;
616 	if (send_intr && !partial_line_sent
617 	    && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0) {
618 	    send_intr = 0;
619 	    if (input_fd) {
620 		/* execute aborted, switch back to fd 0 */
621 		close(input_fd);
622 		input_fd = 0;
623 	    }
624 	}
625 	if (n < 0)
626 	    continue;
627 
628 	/* read player input */
629 	if (FD_ISSET(input_fd, &rdfd) && ring_space(&inbuf)) {
630 	    n = recv_input(input_fd, &inbuf);
631 	    if (n <= 0) {
632 		if (input_fd) {
633 		    /* execute done, switch back to fd 0 */
634 		    if (n < 0) {
635 			perror("read batch file");
636 			send_intr = 1;
637 		    } else
638 			send_eof = 1;
639 		    close(input_fd);
640 		    input_fd = 0;
641 		} else {
642 		    /* stop reading input, drain socket ring buffers */
643 		    if (n < 0)
644 			perror("read stdin");
645 		    send_eof = 1;
646 		    eof_fd0 = 1;
647 		    sa.sa_handler = SIG_DFL;
648 		    sigaction(SIGINT, &sa, NULL);
649 		    send_intr = 0;
650 		}
651 	    } else if (ring_len(&inbuf) > 0)
652 		partial_line_sent = ring_peek(&inbuf, -1) != '\n';
653 	}
654 
655 	/* send it to the server */
656 	if (FD_ISSET(sock, &wrfd)) {
657 	    n = send_input(sock, &inbuf);
658 	    if (n < 0) {
659 		perror("write socket");
660 		break;
661 	    }
662 	}
663 
664 	/* read server output and print it */
665 	if (FD_ISSET(sock, &rdfd)) {
666 	    n = recv_output(sock);
667 	    if (n < 0) {
668 		perror("read socket");
669 		break;
670 	    }
671 	    if (n == 0) {
672 		ret = 0;
673 		break;
674 	    }
675 	}
676     }
677 
678 #ifdef HAVE_LIBREADLINE
679     if (use_readline) {
680 	rl_callback_handler_remove();
681 	if (history_file)
682 	    write_history(history_file);
683     }
684 #endif
685     return ret;
686 }
687 
688 void
prompt(int code,char * prompt,char * teles)689 prompt(int code, char *prompt, char *teles)
690 {
691     char pr[1024];
692 
693     snprintf(pr, sizeof(pr), "%s%s", teles, prompt);
694 #ifdef HAVE_LIBREADLINE
695     if (use_readline) {
696 	rl_set_prompt(pr);
697 	rl_forced_update_display();
698     } else
699 #endif /* HAVE_LIBREADLINE */
700     {
701 	printf("%s", pr);
702 	fflush(stdout);
703     }
704     if (auxfp) {
705 	fprintf(auxfp, "%s%s", teles, prompt);
706 	fflush(auxfp);
707     }
708 }
709