From 8b7d0b915d73a80f63743726a1dc7236f5bffa57 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 17 Nov 2007 14:17:38 +0000 Subject: [PATCH] Rewrite much of client's playing phase code: (EOF_COOKIE, INTR_COOKIE, input_fd, send_intr, recv_output) (recv_input, intr, play): New playing phase code. No native Windows support yet. Sends just one EOF cookie on EOF on standard input instead of up to three. Old servers (before recvclient.c rev. 1.16) fail to terminate the session when they receive an EOF cookie at an argument prompt. The session then hangs; use SIGINT to get out. No longer blocks on sending input, which could deadlock the session. Closes #827090. Fixes error handling for select(). Fixes race condition that could cause server output to be discarded on EOF on standard input. (main): Replace old playing phase code by a call to play(). (intr, sock, interrupt, handleintr): Replaced by play(), remove. [_WIN32] (hStdIn): Ditto. (auxfp): New. (servercmd, prompt, doexecute): Remove parameter auxfi, use auxfp. (eight_bit_clean): Move to servcmd.c. (servercmd): Work on a single non-C_DATA line instead of getting lines from an ioqueue. (servercmd, output, screen, outch): Deal with all ids in servercmd() rather than some there and some in output(). Don't treat C_NOECHO, C_ABORT, C_CMDERR, C_BADCMD specially. Fix C_FLASH and C_EXIT to ignore redirections; they used to ignore them only for some parts. Replace output() by outch(), fold screen into outch(). (servercmd): Truncate long prompts and telegram infos to prevent buffer overflow. (prompt): Use new parameters code, prompt, teles instead of global variables mode, the_prompt, num_teles. (num_teles, the_prompt, mode, nbtu, nmin): Remove. (prompt): Don't write an empty line before argument prompts to auxfp. (servercmd): Don't strip newline from redirections and execute, doredir(), dopipe() and doexecute() need it now. (doredir, dopipe, doexecute): Use new seen_input() instead of gettag(). (doexecute): Set input_fd and leave reading the script file to play(). (serverio, termio, sendeof): Replaced by play(), remove. (LBUF_LEN_MAX, lbuf, lbuf_init, lbuf_len, lbuf_full, lbuf_line) (lbuf_putc): New. (RING_SIZE, ring, ring_init, ring_len, ring_space, ring_peek) (ring_getc, ring_putc, ring_putm, ring_discard, ring_search) (ring_from_file, ring_to_file): New. (clear_recent_input, save_input, seen_input): New. (MAX): New. (ioqueue, io, ioq_init, ioq_dequeue, ioq_read, ioq_write, ioq_qsize) (ioq_drain, ioq_gets, ioqtobuf, enqueuecc, dequeuecc): Unused, remove. (QEMPTY, qelem, insque, remque, initque): Unused, remove. (tagstruct, taglist, io_init, gettag): Unused, remove. --- src/client/Makefile.in | 17 +- src/client/ioqueue.c | 232 -------------------- src/client/linebuf.c | 107 ++++++++++ src/client/{queue.h => linebuf.h} | 26 ++- src/client/main.c | 208 +----------------- src/client/misc.h | 15 +- src/client/play.c | 316 ++++++++++++++++++++++++++++ src/client/ringbuf.c | 245 +++++++++++++++++++++ src/client/{ioqueue.h => ringbuf.h} | 53 ++--- src/client/{queue.c => secure.c} | 42 ++-- src/client/{tags.h => secure.h} | 20 +- src/client/servcmd.c | 286 +++++++++---------------- src/client/serverio.c | 80 ------- src/client/tags.c | 75 ------- src/client/termio.c | 265 ----------------------- 15 files changed, 861 insertions(+), 1126 deletions(-) delete mode 100644 src/client/ioqueue.c create mode 100644 src/client/linebuf.c rename src/client/{queue.h => linebuf.h} (69%) create mode 100644 src/client/play.c create mode 100644 src/client/ringbuf.c rename src/client/{ioqueue.h => ringbuf.h} (53%) rename src/client/{queue.c => secure.c} (67%) rename src/client/{tags.h => secure.h} (83%) delete mode 100644 src/client/serverio.c delete mode 100644 src/client/tags.c delete mode 100644 src/client/termio.c diff --git a/src/client/Makefile.in b/src/client/Makefile.in index ce45e2557..6c8b0a053 100644 --- a/src/client/Makefile.in +++ b/src/client/Makefile.in @@ -53,8 +53,8 @@ srcdir = @srcdir@ VPATH = @srcdir@ prog = empire$E -obj = expect.$O host.$O ioqueue.$O ipglob.$O login.$O main.$O queue.$O \ -servcmd.$O serverio.$O tags.$O termio.$O termlib.$O version.$O +obj = expect.$O host.$O ipglob.$O linebuf.$O login.$O main.$O play.$O \ +ringbuf.$O secure.$O servcmd.$O termlib.$O version.$O all: $(prog) @@ -81,14 +81,13 @@ uninstall: # FIXME generate from .d expect.$O: misc.h host.$O: misc.h -ioqueue.$O: misc.h queue.h ioqueue.h +linebuf.$O: linebuf.h login.$O: misc.h proto.h -main.$O: misc.h proto.h queue.h ioqueue.h tags.h version.h -queue.$O: misc.h queue.h -servcmd.$O: misc.h proto.h queue.h ioqueue.h tags.h -serverio.$O: misc.h queue.h ioqueue.h -tags.$O: misc.h tags.h -termio.$O: misc.h tags.h +main.$O: misc.h version.h +play.$O: linebuf.h misc.h proto.h ringbuf.h secure.h +ringbuf.$O: ringbuf.h +secure.$O: ringbuf.h secure.h +servcmd.$O: misc.h proto.h secure.h termlib.$O: misc.h version.$O: version.h $(obj): config.h diff --git a/src/client/ioqueue.c b/src/client/ioqueue.c deleted file mode 100644 index 872e6ad58..000000000 --- a/src/client/ioqueue.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * ioqueue.c: Manage an i/o queue - * - * Known contributors to this file: - * Steve McClure, 1998 - */ - -#include - -#include -#include -#include -#include "ioqueue.h" -#include "misc.h" -#include "queue.h" - -static int ioqtobuf(struct ioqueue *ioq, char *buf, int cc); -static void enqueuecc(struct ioqueue *ioq, char *buf, int cc); -static int dequeuecc(struct ioqueue *ioq, int cc); - - -void -ioq_init(struct ioqueue *ioq, int bsize) -{ - initque(&ioq->queue); - ioq->cc = 0; - ioq->bsize = bsize; -} - -/* - * Copy the specified number of characters into the buffer - * provided, without actually dequeueing the data. Return - * number of bytes actually found. - */ -int -ioq_peek(struct ioqueue *ioq, char *buf, int cc) -{ - return ioqtobuf(ioq, buf, cc); -} - -int -ioq_dequeue(struct ioqueue *ioq, int cc) -{ - if (dequeuecc(ioq, cc) != cc) - return 0; - return cc; -} - -int -ioq_read(struct ioqueue *ioq, char *buf, int cc) -{ - int n; - - n = ioqtobuf(ioq, buf, cc); - if (n > 0) - dequeuecc(ioq, n); - return n; -} - -void -ioq_write(struct ioqueue *ioq, char *buf, int cc) -{ - enqueuecc(ioq, buf, cc); -} - -int -ioq_qsize(struct ioqueue *ioq) -{ - return ioq->cc; -} - -void -ioq_drain(struct ioqueue *ioq) -{ - struct io *io; - struct qelem *qp; - - while ((qp = ioq->queue.q_forw) != &ioq->queue) { - io = (struct io *)qp; - free(io->data); - (void)remque(&io->queue); - (void)free(io); - } - ioq->cc = 0; -} - -char * -ioq_gets(struct ioqueue *ioq, char *buf, int cc, int *eol) -{ - char *p; - char *end; - int nbytes; - - *eol = 0; - - cc--; - - nbytes = ioqtobuf(ioq, buf, cc); - end = &buf[nbytes]; - for (p = buf; p < end && *p; p++) { - if (*p == '\n') { - *++p = '\0'; - *eol = 1; - dequeuecc(ioq, p - buf); - return buf; - } - } - if (cc && (p - buf) == cc) { - dequeuecc(ioq, cc); - buf[cc] = '\0'; - return buf; - } - return NULL; -} - -/* - * all the rest are local to this module - */ - - -/* - * copy cc bytes from ioq to buf. - * this routine doesn't free memory; this is - * left for a higher level. - */ -static int -ioqtobuf(struct ioqueue *ioq, char *buf, int cc) -{ - struct io *io; - struct qelem *qp; - char *offset; - int nbytes; - int nleft; - - nleft = cc; - offset = buf; - for (qp = ioq->queue.q_forw; qp != &ioq->queue; qp = qp->q_forw) { - io = (struct io *)qp; - if ((nbytes = io->nbytes - io->offset) < 0) { - fprintf(stderr, "ioqtobuf: offset %d nbytes %d\n", - io->offset, io->nbytes); - continue; - } - if (nbytes > 0) { - if (nleft < nbytes) - nbytes = nleft; - memcpy(offset, io->data + io->offset, nbytes); - offset += nbytes; - nleft -= nbytes; - } - } - return offset - buf; -} - -/* - * append a buffer to the end of the ioq. - */ -static void -enqueuecc(struct ioqueue *ioq, char *buf, int cc) -{ - struct io *io; - - io = malloc(sizeof(*io)); - io->nbytes = cc; - io->offset = 0; - io->data = buf; - insque(&io->queue, ioq->queue.q_back); - ioq->cc += cc; -} - -/* - * remove cc bytes from ioqueue ioq - * free memory, dequeue io elements - * which are no longer used. - */ -static int -dequeuecc(struct ioqueue *ioq, int cc) -{ - struct io *io; - struct qelem *qp; - int nbytes; - int there; - - nbytes = 0; - while ((qp = ioq->queue.q_forw) != &ioq->queue) { - io = (struct io *)qp; - there = io->nbytes - io->offset; - if (there < 0) { - fprintf(stderr, "dequeuecc: nbytes %d, offset %d\n", - io->nbytes, io->offset); - continue; - } - if (cc > there) { - cc -= there; - nbytes += there; - (void)remque(&io->queue); - free(io->data); - } else { - io->offset += cc; - nbytes += cc; - break; - } - } - ioq->cc -= nbytes; - return nbytes; -} diff --git a/src/client/linebuf.c b/src/client/linebuf.c new file mode 100644 index 000000000..4187bd50f --- /dev/null +++ b/src/client/linebuf.c @@ -0,0 +1,107 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * linebuf.c: Simple line buffer + * + * Known contributors to this file: + * Markus Armbruster, 2007 + */ + +#include + +#include +#include +#include "linebuf.h" + +/* + * Initialize empty line buffer. + * Not necessary if *LBUF is already zeroed. + */ +void +lbuf_init(struct lbuf *lbuf) +{ + lbuf->len = lbuf->full = 0; +} + +/* + * Return length of currently buffered line. + * This includes the newline if present. + */ +int +lbuf_len(struct lbuf *lbuf) +{ + return lbuf->len; +} + +/* + * Is LBUF full (i.e. we got the newline)? + */ +int +lbuf_full(struct lbuf *lbuf) +{ + return lbuf->full; +} + +/* + * Return a pointer to the currently buffered line. + * If you mess with the line in a way that changes the line length, + * better call lbuf_init() next. + */ +char * +lbuf_line(struct lbuf *lbuf) +{ + assert(lbuf->len < sizeof(lbuf->line)); + lbuf->line[lbuf->len] = 0; + return lbuf->line; +} + +/* + * Append CH to the line buffered in LBUF. + * LBUF must not be full. + * If CH is a newline, the buffer is now full. Return the line + * length, including the newline. + * Else return 0 if there was space, and -1 if not. + */ +int +lbuf_putc(struct lbuf *lbuf, char ch) +{ + assert(!lbuf->full); + + if (ch == '\n') { + assert(lbuf->len + 1 < sizeof(lbuf->line)); + lbuf->line[lbuf->len++] = ch; + lbuf->line[lbuf->len] = 0; + lbuf->full = 1; + return lbuf->len; + } + + if (lbuf->len + 2 >= sizeof(lbuf->line)) + return -1; /* truncating long line */ + + lbuf->line[lbuf->len++] = ch; + return 0; +} diff --git a/src/client/queue.h b/src/client/linebuf.h similarity index 69% rename from src/client/queue.h rename to src/client/linebuf.h index 9db972b44..4149adf2a 100644 --- a/src/client/queue.h +++ b/src/client/linebuf.h @@ -25,24 +25,28 @@ * * --- * - * queue.h: generic vax-like doubly linked list queues. + * linebuf.h: Simple line buffer * * Known contributors to this file: - * + * Markus Armbruster, 2007 */ -#ifndef QUEUE_H -#define QUEUE_H +#ifndef LINEBUF_H +#define LINEBUF_H -#define QEMPTY(p) ((p)->q_forw == (p)) +#define LBUF_LEN_MAX 4096 -struct qelem { - struct qelem *q_forw; - struct qelem *q_back; +struct lbuf { + /* All members are private! */ + unsigned len; /* strlen(line) */ + int full; /* got a complete line, with newline? */ + char line[LBUF_LEN_MAX]; /* buffered line, zero-terminated */ }; -void insque(struct qelem *p, struct qelem *q); -void remque(struct qelem *p); -void initque(struct qelem *p); +extern void lbuf_init(struct lbuf *); +extern int lbuf_len(struct lbuf *); +extern int lbuf_full(struct lbuf *); +extern char *lbuf_line(struct lbuf *); +extern int lbuf_putc(struct lbuf *, char); #endif diff --git a/src/client/main.c b/src/client/main.c index 754f6b0f8..cd0f6fc25 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -36,39 +36,12 @@ #include -#include -#include -#include -#include -#include -#if defined(_WIN32) -#include -#else #include -#include -#include +#include #include -#endif - -#include "ioqueue.h" #include "misc.h" -#include "proto.h" -#include "tags.h" #include "version.h" -#ifdef _WIN32 -HANDLE hStdIn; -#endif - -#define RETRY 3 - -int eight_bit_clean; -int sock; - -static volatile sig_atomic_t interrupt; -static void intr(int sig); -static int handleintr(int); - static void print_usage(char *program_name) { @@ -94,25 +67,10 @@ main(int argc, char **argv) char *uname; char *host; char *port; + int sock; #ifdef _WIN32 - WORD wVersionRequested; - WSADATA WsaData; - int err; - fd_set readfds; - struct timeval tm; - DWORD stdinmode; - SECURITY_ATTRIBUTES security; - int bRedirected = 0; char unamebuf[128]; -#else - struct sigaction sa; - fd_set mask; - fd_set savemask; - int retry = 0; #endif - struct ioqueue server; - FILE *auxout_fp = NULL; - int n; #ifdef _WIN32 /* @@ -124,9 +82,6 @@ main(int argc, char **argv) * after each prompt is required. */ setvbuf(stdout, NULL, _IOLBF, 4096); -#else - FD_ZERO(&mask); - FD_ZERO(&savemask); #endif while ((opt = getopt(argc, argv, "2:kuhv")) != EOF) { @@ -192,14 +147,13 @@ main(int argc, char **argv) } getsose(); - if (auxfname && (auxout_fp = fopen(auxfname, "a")) == NULL) { + if (auxfname && (auxfp = fopen(auxfname, "a")) == NULL) { fprintf(stderr, "Unable to open %s for append\n", auxfname); exit(1); } #ifdef _WIN32 - wVersionRequested = MAKEWORD(2, 0); - err = WSAStartup(wVersionRequested, &WsaData); + err = WSAStartup(MAKEWORD(2, 0), &WsaData); if (err != 0) { printf("WSAStartup Failed, error code %d\n", err); exit(1); @@ -211,156 +165,8 @@ main(int argc, char **argv) if (!login(sock, uname, country, passwd, send_kill, utf8)) exit(1); - ioq_init(&server, 2048); - io_init(); -#ifndef _WIN32 - FD_ZERO(&mask); - FD_SET(0, &savemask); - FD_SET(sock, &savemask); - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = intr; - sigaction(SIGINT, &sa, NULL); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); - while (FD_ISSET(sock, &savemask)) { - mask = savemask; - n = select(sock + 1, &mask, NULL, NULL, NULL); - if (interrupt) { - if (!handleintr(sock)) - break; - errno = 0; - } - if (n <= 0) { - if (errno == EINTR) { - perror("select"); - (void)close(sock); - FD_CLR(sock, &savemask); - } - } else { - if (FD_ISSET(0, &mask)) { - if (!termio(0, sock, auxout_fp)) { - if (retry++ >= RETRY) { - FD_CLR(0, &savemask); - } - } else { - retry = 0; - } - } - if (FD_ISSET(sock, &mask)) { - if (!serverio(sock, &server)) - FD_CLR(sock, &savemask); - else - servercmd(&server, auxout_fp); - } - } - } -#else /* _WIN32 */ - signal(SIGINT, intr); - - bRedirected = 0; - tm.tv_sec = 0; - tm.tv_usec = 1000; - - if (!isatty(fileno(stdin))) - bRedirected = 1; - else { - security.nLength = sizeof(SECURITY_ATTRIBUTES); - security.lpSecurityDescriptor = NULL; - security.bInheritHandle = TRUE; - hStdIn = CreateFile("CONIN$", - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &security, OPEN_EXISTING, (DWORD) NULL, NULL); - - if (hStdIn == INVALID_HANDLE_VALUE) { - printf("Error getting hStdIn.\n"); - fflush(stdout); - exit(-3); - } - - err = GetConsoleMode(hStdIn, &stdinmode); - if (!err) { - printf("Error getting console mode.\n"); - fflush(stdout); - exit(-4); - } else { - stdinmode |= ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT; - err = SetConsoleMode(hStdIn, stdinmode); - if (!err) { - printf("Error setting console mode.\n"); - fflush(stdout); - exit(-5); - } - } - } - while (1) { - FD_ZERO(&readfds); - FD_SET(sock, &readfds); - n = select(sock + 1, &readfds, NULL, NULL, &tm); - if (interrupt) { - if (!handleintr(sock)) - break; - errno = 0; - } - if (n < 0) { - if (errno == EINTR) { - errno = WSAGetLastError(); - perror("select"); - (void)closesocket(sock); - break; - } - } else { - if (bRedirected == 1) { - if (!termio(0, sock, auxout_fp)) - bRedirected = -1; - } else if (bRedirected == 0) { - if (WaitForSingleObject(hStdIn, 10) != WAIT_TIMEOUT) { - termio(-1, sock, auxout_fp); - FlushConsoleInputBuffer(hStdIn); - } - } - if (FD_ISSET(sock, &readfds)) { - if (!serverio(sock, &server)) - break; - else - servercmd(&server, auxout_fp); - } - } - } - if (bRedirected == 0) - CloseHandle(hStdIn); -#endif /* _WIN32 */ - ioq_drain(&server); -#ifdef _WIN32 - (void)closesocket(sock); -#else - (void)close(sock); -#endif - return 0; /* Shut the compiler up */ -} - -static void -intr(int sig) -{ - interrupt = 1; -#ifdef _WIN32 - signal(SIGINT, intr); -#endif -} + if (play(sock) < 0) + exit(1); -static int -handleintr(int s) -{ - if (interrupt) { - /* tacky, but it works */ -#if !defined(_WIN32) - if (write(s, "\naborted\n", 1 + 7 + 1) <= 0) -#else - if (send(s, "\naborted\n", 1 + 7 + 1, 0) <= 0) -#endif - return 0; - interrupt = 0; - } - return 1; + return 0; } diff --git a/src/client/misc.h b/src/client/misc.h index 2dc3bbd3c..ee9c9e064 100644 --- a/src/client/misc.h +++ b/src/client/misc.h @@ -39,19 +39,16 @@ #include #endif -struct ioqueue; +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) extern char empirehost[]; extern char empireport[]; extern int eight_bit_clean; -extern int sock; +extern int input_fd; +extern FILE *auxfp; extern char *SO; extern char *SE; -#ifdef _WIN32 -extern HANDLE hStdIn; -#endif - #ifdef _WIN32 #define getsose() ((void)0) #define putso() ((void)0) @@ -67,9 +64,9 @@ int recvline(int s, char *buf); int expect(int s, int match, char *buf); int tcp_connect(char *, char *); int login(int s, char *uname, char *cname, char *cpass, int kill_proc, int); +int play(int); void sendcmd(int s, char *cmd, char *arg); -void servercmd(struct ioqueue *ioq, FILE *auxfi); -int serverio(int s, struct ioqueue *ioq); -int termio(int fd, int sock, FILE *auxfi); +void servercmd(int, char *, int); +void outch(char); #endif diff --git a/src/client/play.c b/src/client/play.c new file mode 100644 index 000000000..c3fe98e27 --- /dev/null +++ b/src/client/play.c @@ -0,0 +1,316 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * play.c: Playing the game + * + * Known contributors to this file: + * Markus Armbruster, 2007 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "linebuf.h" +#include "misc.h" +#include "proto.h" +#include "ringbuf.h" +#include "secure.h" + +#define EOF_COOKIE "ctld\n" +#define INTR_COOKIE "\naborted\n" + +int input_fd; +static volatile sig_atomic_t send_intr; /* need to send INTR_COOKIE */ + +/* + * Receive and process server output from SOCK. + * Return number of characters received on success, -1 on error. + */ +static int +recv_output(int sock) +{ + /* + * Read a chunk of server output and feed its characters into a + * simple state machine. + * Initial state is SCANNING_ID. + * In state SCANNING_ID, buffer the character. If it's a space, + * decode the id that has been buffered, and enter state BUFFERING + * or COPYING depending on its value. + * In state BUFFERING, buffer the character. If it's newline, + * pass id and buffered text to servercmd(), then enter state + * SCANNING_ID. + * In state COPYING, pass the character to outch(). If it's + * newline, enter state SCANNING_ID. + */ + static enum { + SCANNING_ID, BUFFERING, COPYING + } state = SCANNING_ID; + static int id; + static struct lbuf lbuf; + char buf[4096]; + ssize_t n; + int i, ch, len; + char *line, *end; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) + return -1; + + for (i = 0; i < n; i++) { + ch = buf[i]; + switch (state) { + case SCANNING_ID: + if (ch == '\n') { + /* FIXME gripe unexpected! */ + lbuf_init(&lbuf); + break; + } + if (ch != ' ') { + lbuf_putc(&lbuf, ch); + break; + } + line = lbuf_line(&lbuf); + id = strtol(line, &end, 16); + if (end == line || *end) { + /* FIXME gripe bad id */ + id = -1; + } + lbuf_init(&lbuf); + + switch (id) { + case C_PROMPT: + case C_FLUSH: + case C_EXECUTE: + case C_EXIT: + case C_FLASH: + case C_INFORM: + case C_PIPE: + case C_REDIR: + state = BUFFERING; + break; + default: + /* unknown or unexpected id, treat like C_DATA */ + case C_DATA: + state = COPYING; + break; + } + break; + + case BUFFERING: + len = lbuf_putc(&lbuf, ch); + if (len) { + line = lbuf_line(&lbuf); + servercmd(id, line, len); + lbuf_init(&lbuf); + state = SCANNING_ID; + } + break; + + case COPYING: + outch(ch); + if (ch == '\n') + state = SCANNING_ID; + } + } + + return n; +} + +/* + * Receive command input from FD into INBUF. + * Return 1 on receipt of input, zero on EOF, -1 on error. + */ +static int +recv_input(int fd, struct ring *inbuf) +{ + static struct lbuf cmdbuf; + int n, i, ch; + char *line; + int res = 1; + + n = ring_from_file(inbuf, fd); + if (n < 0) + return -1; + if (n == 0) { + /* EOF on input */ + if (lbuf_len(&cmdbuf)) { + /* incomplete line */ + ring_putc(inbuf, '\n'); + n++; + } + /* + * Can't put EOF cookie into INBUF here, it may not fit. + * Leave it to caller. + */ + res = 0; + } + + /* copy input to AUXFP etc. */ + for (i = -n; i < 0; i++) { + ch = ring_peek(inbuf, i); + assert(ch != EOF); + if (lbuf_putc(&cmdbuf, ch)) { + line = lbuf_line(&cmdbuf); + if (auxfp) + fputs(line, auxfp); + save_input(line); + lbuf_init(&cmdbuf); + } + } + + return res; +} + +static void +intr(int sig) +{ + send_intr = 1; +#ifdef _WIN32 + signal(SIGINT, intr); +#endif +} + +/* + * Play on SOCK. + * The session must be in the playing phase. + * Return 0 when the session ended, -1 on error. + */ +int +play(int sock) +{ + /* + * Player input flows from INPUT_FD through recv_input() into ring + * buffer INBUF, which drains into SOCK. This must not block. + * Server output flows from SOCK into recv_output(). Reading SOCK + * must not block. + */ + struct sigaction sa; + struct ring inbuf; /* input buffer, draining to SOCK */ + int eof_fd0; /* read fd 0 hit EOF? */ + int send_eof; /* need to send EOF_COOKIE */ + fd_set rdfd, wrfd; + int n; + + sa.sa_flags = 0; + sa.sa_handler = intr; + sigaction(SIGINT, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + + ring_init(&inbuf); + eof_fd0 = send_eof = 0; + + for (;;) { + FD_ZERO(&rdfd); + FD_ZERO(&wrfd); + + /* + * Want to read player input only when we don't need to send + * cookies, and we haven't hit EOF on fd 0, and INBUF can + * accept some. + */ + if (!send_intr && !send_eof && !eof_fd0 && ring_space(&inbuf)) + FD_SET(input_fd, &rdfd); + /* Want to send player input only when we have something */ + if (send_intr || send_eof || ring_len(&inbuf)) + FD_SET(sock, &wrfd); + /* Always want to read server output */ + FD_SET(sock, &rdfd); + + n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL); + if (n < 0) { + if (errno != EINTR) { + perror("select"); + return -1; + } + } + + if (send_eof + && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0) + send_eof = 0; + if (send_intr + && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0) + send_intr = 0; + + if (n < 0) + continue; + + /* read player input */ + if (FD_ISSET(input_fd, &rdfd)) { + n = recv_input(input_fd, &inbuf); + if (n < 0) { + perror("read stdin"); /* FIXME stdin misleading, could be execing */ + return -1; + } + if (n == 0) { + /* EOF on input */ + send_eof = 1; + if (input_fd) { + /* execute done, switch back to fd 0 */ + input_fd = 0; + } else { + /* stop reading input, drain socket ring buffers */ + eof_fd0 = 1; + sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &sa, NULL); + } + } + } + + /* send it to the server */ + if (FD_ISSET(sock, &wrfd)) { + n = ring_to_file(&inbuf, sock); + if (n < 0) { + perror("write socket"); + return -1; + } + } + + /* read server output and print it */ + if (FD_ISSET(sock, &rdfd)) { + n = recv_output(sock); + if (n < 0) { + perror("read socket"); + return -1; + } + if (n == 0) + return 0; + if (input_fd < 0) { + /* execute failed */ + input_fd = 0; + send_eof = 1; + } + } + } +} diff --git a/src/client/ringbuf.c b/src/client/ringbuf.c new file mode 100644 index 000000000..b893bc103 --- /dev/null +++ b/src/client/ringbuf.c @@ -0,0 +1,245 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * ringbuf.c: Simple ring buffer + * + * Known contributors to this file: + * Markus Armbruster, 2007 + */ + +#include + +#include +#include +#include +#include +#include +#include "ringbuf.h" + +/* + * Initialize empty ring buffer. + * Not necessary if *R is already zeroed. + */ +void +ring_init(struct ring *r) +{ + r->cons = r->prod = 0; +} + +/* + * Return number of bytes held in ring buffer. + */ +int +ring_len(struct ring *r) +{ + assert(r->prod - r->cons <= RING_SIZE); + return r->prod - r->cons; +} + +/* + * Return how much space is left in ring buffer. + */ +int +ring_space(struct ring *r) +{ + return RING_SIZE - (r->prod - r->cons); +} + +/* + * Peek at ring buffer contents. + * N must be between -RING_SIZE-1 and RING_SIZE. + * If N>=0, peek at the (N+1)-th byte to be gotten. + * If N<0, peek at the -N-th byte that has been put in. + * Return the byte as unsigned char coverted to int, or EOF if there + * aren't that many bytes in the ring buffer. + */ +int +ring_peek(struct ring *r, int n) +{ + unsigned idx; + + assert(-RING_SIZE - 1 <= n && n <= RING_SIZE); + + idx = n >= 0 ? r->cons + n : r->prod - -n; + if (idx < r->cons && idx >= r->prod) + return EOF; + return r->buf[idx % RING_SIZE]; +} + +/* + * Get and remove the oldest byte from the ring buffer. + * Return it as unsigned char coverted to int, or EOF if the buffer was + * empty. + */ +int +ring_getc(struct ring *r) +{ + if (r->cons == r->prod) + return EOF; + return r->buf[r->cons++ % RING_SIZE]; +} + +/* + * Attempt to put byte C into the ring buffer. + * Return EOF when the buffer is full, else C. + */ +int +ring_putc(struct ring *r, unsigned char c) +{ + if (r->prod - r->cons == RING_SIZE) + return EOF; + return r->buf[r->prod++ % RING_SIZE] = c; +} + +/* + * Attempt to put SZ bytes from BUF into the ring buffer. + * Return space left in ring buffer when it fits, else don't change + * the ring buffer and return how much space is missing. + */ +int +ring_putm(struct ring *r, void *buf, size_t sz) +{ + char *p = buf; + int left = ring_space(r) - sz; + int res; + size_t i; + + if (left >= 0) { + res = 0; + for (i = 0; i < sz; i++) + res = ring_putc(r, p[i]); + assert(res != EOF); + } + + return left; +} + +/* + * Discard the N oldest bytes from the ring buffer. + * It must hold at least that many. + */ +void +ring_discard(struct ring *r, int n) +{ + assert(n <= ring_len(r)); + r->cons += n; +} + +/* + * Search the ring buffer for zero-terminated string S. + * If found, return a non-negative value referring to the beginning of + * S in the buffer when passed to ring_peek(). Else return -1. + */ +int +ring_search(struct ring *r, char *s) +{ + size_t len = strlen(s); + size_t i, j; + + for (i = r->cons; i + len <= r->prod; i++) { + for (j = 0; j < len && s[j] == r->buf[(i + j) % RING_SIZE]; j++) ; + if (!s[j]) + return i - r->cons; + } + return -1; +} + +/* + * Fill ring buffer from file referred by file descriptor FD. + * If ring buffer is already full, do nothing and return 0. + * Else attempt to read as many bytes as space permits, with readv(), + * and return its value. + */ +int +ring_from_file(struct ring *r, int fd) +{ + unsigned cons = r->cons % RING_SIZE; + unsigned prod = r->prod % RING_SIZE; + struct iovec iov[2]; + int cnt; + ssize_t res; + + if (r->prod == r->cons + RING_SIZE) + return 0; + + iov[0].iov_base = r->buf + prod; + if (cons <= prod) { + /* r->buf[prod..] */ + iov[0].iov_len = RING_SIZE - prod; + /* r->buf[..cons-1] */ + iov[1].iov_base = r->buf; + iov[1].iov_len = cons; + cnt = 2; + } else { + /* r->buf[prod..cons-1] */ + iov[0].iov_len = cons - prod; + cnt = 1; + } + res = readv(fd, iov, cnt); + if (res < 0) + return res; + r->prod += res; + return res; +} + +/* + * Drain ring buffer to file referred by file descriptor FD. + * If ring buffer is already empty, do nothing and return 0. + * Else attempt to write complete contents with writev(), and return + * its value. + */ +int +ring_to_file(struct ring *r, int fd) +{ + unsigned cons = r->cons % RING_SIZE; + unsigned prod = r->prod % RING_SIZE; + struct iovec iov[2]; + int cnt; + ssize_t res; + + if (r->cons == r->prod) + return 0; + + iov[0].iov_base = r->buf + cons; + if (prod <= cons) { + /* r->buf[cons..] */ + iov[0].iov_len = RING_SIZE - cons; + /* r->buf[..prod-1] */ + iov[1].iov_base = r->buf; + iov[1].iov_len = prod; + cnt = 2; + } else { + /* r->buf[cons..prod-1] */ + iov[0].iov_len = prod - cons; + cnt = 1; + } + res = writev(fd, iov, cnt); + if (res < 0) + return res; + r->cons += res; + return res; +} diff --git a/src/client/ioqueue.h b/src/client/ringbuf.h similarity index 53% rename from src/client/ioqueue.h rename to src/client/ringbuf.h index 9a88c385e..0dc086604 100644 --- a/src/client/ioqueue.h +++ b/src/client/ringbuf.h @@ -25,38 +25,43 @@ * * --- * - * ioqueue.h: stores and frees data associated with a file descriptor. - * uses writev to write, and read to read. + * ringbuf.h: Simple ring buffer * * Known contributors to this file: - * + * Markus Armbruster, 2007 */ -#ifndef IOQUEUE_H -#define IOQUEUE_H +#ifndef RINGBUF_H +#define RINGBUF_H -#include "queue.h" +#include -struct ioqueue { - struct qelem queue; /* queue fwd/back */ - int bsize; /* basic block size */ - int cc; /* character count */ -}; +#define RING_SIZE 4096 -struct io { - struct qelem queue; /* list of ioqueue elements */ - int nbytes; /* number of data bytes present */ - int offset; /* offset into current entry */ - char *data; /* pointer to start */ +/* Ring buffer, consumer reads, producer writes */ +struct ring { + /* All members are private! */ + /* + * Consumer reads from buf[cons % RING_SIZE], incrementing cons + * Produces writes to buf[prod % RING_SIZE], incrementing prod + * prod == cons: empty + * prod == cons + RING_SIZE: full + * invariant: prod - cons <= RING_SIZE + */ + unsigned cons, prod; + unsigned char buf[RING_SIZE]; }; -void ioq_init(struct ioqueue *ioq, int bsize); -int ioq_peek(struct ioqueue *ioq, char *buf, int cc); -int ioq_dequeue(struct ioqueue *ioq, int cc); -int ioq_read(struct ioqueue *ioq, char *buf, int cc); -void ioq_write(struct ioqueue *ioq, char *buf, int cc); -int ioq_qsize(struct ioqueue *ioq); -void ioq_drain(struct ioqueue *ioq); -char *ioq_gets(struct ioqueue *ioq, char *buf, int cc, int *eol); +extern void ring_init(struct ring *); +extern int ring_len(struct ring *); +extern int ring_space(struct ring *); +extern int ring_peek(struct ring *, int); +extern int ring_getc(struct ring *); +extern int ring_putc(struct ring *, unsigned char); +extern int ring_putm(struct ring *, void *, size_t); +extern void ring_discard(struct ring *, int); +extern int ring_search(struct ring *, char *); +extern int ring_from_file(struct ring *, int fd); +extern int ring_to_file(struct ring *, int fd); #endif diff --git a/src/client/queue.c b/src/client/secure.c similarity index 67% rename from src/client/queue.c rename to src/client/secure.c index 557ff688e..258ee313c 100644 --- a/src/client/queue.c +++ b/src/client/secure.c @@ -25,35 +25,41 @@ * * --- * - * queue.c: implementation of various queuing routines + * secure.c: Check redir etc. to protect against tampering deity * * Known contributors to this file: - * Steve McClure, 1998 + * Markus Armbruster, 2007 */ #include -#include "queue.h" +#include +#include "ringbuf.h" +#include "secure.h" -void -insque(struct qelem *p, struct qelem *q) -{ - p->q_forw = q->q_forw; - p->q_back = q; - q->q_forw->q_back = p; - q->q_forw = p; -} +struct ring recent_input; void -remque(struct qelem *p) +save_input(char *inp) { - p->q_back->q_forw = p->q_forw; - p->q_forw->q_back = p->q_back; + size_t len = strlen(inp); + int left; + + left = ring_putm(&recent_input, inp, len); + if (left < 0) { + ring_discard(&recent_input, ring_search(&recent_input, "\n")); + ring_putm(&recent_input, inp, len); + } } -void -initque(struct qelem *p) +int +seen_input(char *tail) { - p->q_forw = p; - p->q_back = p; + int dist = ring_search(&recent_input, tail); + + if (dist < 0) + return 0; + + ring_discard(&recent_input, dist + strlen(tail)); + return 1; } diff --git a/src/client/tags.h b/src/client/secure.h similarity index 83% rename from src/client/tags.h rename to src/client/secure.h index 9da5e3483..a92026eb3 100644 --- a/src/client/tags.h +++ b/src/client/secure.h @@ -25,23 +25,17 @@ * * --- * - * tags.h: Tag structure definition + * secure.h: Check redir etc. to protect against tampering deity * * Known contributors to this file: - * John Yockey, 1998 + * Markus Armbruster, 2007 */ -#ifndef TAGS_H -#define TAGS_H +#ifndef SECURE_H +#define SECURE_H -struct tagstruct { - char *item; - struct tagstruct *next; -}; - -extern struct tagstruct *taglist; - -void io_init(void); -char *gettag(char *p); +extern void clear_recent_input(void); +extern void save_input(char *); +extern int seen_input(char *); #endif diff --git a/src/client/servcmd.c b/src/client/servcmd.c index 24bae6084..f4129b138 100644 --- a/src/client/servcmd.c +++ b/src/client/servcmd.c @@ -35,119 +35,88 @@ #include +#include #include #include #include #include -#include #include -#ifdef _WIN32 -#include -#else -#include -#endif - -#include "ioqueue.h" #include "misc.h" #include "proto.h" -#include "tags.h" +#include "secure.h" + +int eight_bit_clean; +FILE *auxfp; -static char num_teles[64]; -static char the_prompt[1024]; -static int mode; -static int nbtu; -static int nmin; static FILE *redir_fp; static FILE *pipe_fp; -static void prompt(FILE *auxfi); +static void prompt(int, char *, char *); static void doredir(char *p); static void dopipe(char *p); -static void doexecute(char *p, FILE *auxfi); -static void output(int code, char *buf, FILE *auxfi); -static void screen(char *buf); +static void doexecute(char *p); void -servercmd(struct ioqueue *ioq, FILE *auxfi) +servercmd(int code, char *arg, int len) { - char buf[1024]; - char *p; - static int code = -1; - int eol; + static int nmin, nbtu; + static char the_prompt[1024]; + static char teles[64]; - while (ioq_gets(ioq, buf, sizeof(buf), &eol)) { - p = buf; - if (code == -1) { - if (isalpha(*buf)) - code = 10 + (*buf - 'a'); - else - code = *buf - '0'; - while (*p && !isspace(*p)) - p++; - *p++ = 0; + switch (code) { + case C_PROMPT: + if (sscanf(arg, "%d %d", &nmin, &nbtu) != 2) { + fprintf(stderr, "prompt: bad server prompt %s\n", arg); } - /* - * FIXME - * C_REDIR, C_PIPE, and C_EXECUTE will not - * work with filename longer than one buffer - */ - switch (code) { - case C_PROMPT: - if (sscanf(p, "%d %d", &nmin, &nbtu) != 2) { - fprintf(stderr, "prompt: bad server prompt %s\n", p); + snprintf(the_prompt, sizeof(the_prompt), "[%d:%d] Command : ", + nmin, nbtu); + prompt(code, the_prompt, teles); + break; + case C_FLUSH: + snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg); + prompt(code, the_prompt, teles); + break; + case C_EXECUTE: + doexecute(arg); + break; + case C_EXIT: + printf("Exit: %s", arg); + if (auxfp) + fprintf(auxfp, "Exit: %s", arg); + break; + case C_FLASH: + printf("\n%s", arg); + if (auxfp) + fprintf(auxfp, "\n%s", arg); + break; + case C_INFORM: + if (*arg) { + snprintf(teles, sizeof(teles), "(%.*s )", len -1, arg); + if (!redir_fp && !pipe_fp) { + putchar('\07'); + prompt(code, the_prompt, teles); } - mode = code; - sprintf(the_prompt, "[%d:%d] Command : ", nmin, nbtu); - prompt(auxfi); - break; - case C_REDIR: - if (eol) - p[strlen(p) - 1] = '\0'; - doredir(p); - break; - case C_PIPE: - if (eol) - p[strlen(p) - 1] = '\0'; - dopipe(p); - break; - case C_FLUSH: - mode = code; - if (eol) - p[strlen(p) - 1] = '\0'; - sprintf(the_prompt, "%s", p); - prompt(auxfi); - break; - case C_EXECUTE: - if (eol) - p[strlen(p) - 1] = '\0'; - doexecute(p, auxfi); - break; - case C_INFORM: - if (eol) - p[strlen(p) - 1] = '\0'; - if (*p) { - p[strlen(p) - 1] = '\0'; - sprintf(num_teles, "(%s) ", p + 1); - if (!redir_fp && !pipe_fp) { - putchar('\07'); - prompt(NULL); - } - } else - *num_teles = '\0'; - break; - default: - output(code, p, auxfi); - break; - } - if (eol) - code = -1; + } else + teles[0] = 0; + break; + case C_PIPE: + dopipe(arg); + break; + case C_REDIR: + doredir(arg); + break; + default: + assert(0); + break; } } static void -prompt(FILE *auxfi) +prompt(int code, char *prompt, char *teles) { - if (mode == C_PROMPT) { + char *nl; + + if (code == C_PROMPT) { if (redir_fp) { (void)fclose(redir_fp); redir_fp = NULL; @@ -156,13 +125,12 @@ prompt(FILE *auxfi) pipe_fp = NULL; } } - if (mode == C_PROMPT) - printf("\n"); - printf("%s%s", num_teles, the_prompt); - (void)fflush(stdout); - if (auxfi) { - fprintf(auxfi, "\n%s%s", num_teles, the_prompt); - (void)fflush(auxfi); + nl = code == C_PROMPT || code == C_INFORM ? "\n" : ""; + printf("%s%s%s", nl, teles, prompt); + fflush(stdout); + if (auxfp) { + fprintf(auxfp, "%s%s%s", nl, teles, prompt); + fflush(auxfp); } } @@ -183,7 +151,6 @@ fname(char *s) static void doredir(char *p) { - char *tag; int mode; int fd; @@ -192,6 +159,12 @@ doredir(char *p) redir_fp = NULL; } + if (!seen_input(p)) { + fprintf(stderr, "WARNING! Server attempted to redirect %s\n", + p); + return; + } + if (*p++ != '>') { fprintf(stderr, "WARNING! Weird redirection %s", p); return; @@ -207,19 +180,12 @@ doredir(char *p) } else mode |= O_EXCL; - tag = gettag(p); p = fname(p); - if (tag == NULL) { - fprintf(stderr, "WARNING! Server redirected output to file %s\n", - p); - return; - } - free(tag); - if (*p == 0) { fprintf(stderr, "Redirection lacks a file name\n"); return; } + fd = open(p, mode, 0600); redir_fp = fd < 0 ? NULL : fdopen(fd, "w"); if (!redir_fp) { @@ -234,25 +200,22 @@ doredir(char *p) static void dopipe(char *p) { - char *tag; - - if (*p++ != '|') { - fprintf(stderr, "WARNING! Weird pipe %s", p); + if (!seen_input(p)) { + fprintf(stderr, "WARNING! Server attempted to pipe %s", p); return; } - tag = gettag(p); - if (tag == NULL) { - fprintf(stderr, "WARNING! Server attempted to run: %s\n", p); + if (*p++ != '|') { + fprintf(stderr, "WARNING! Weird pipe %s", p); return; } - free(tag); for (; *p && isspace(*p); p++) ; if (*p == 0) { fprintf(stderr, "Redirection lacks a command\n"); return; } + if ((pipe_fp = popen(p, "w")) == NULL) { fprintf(stderr, "Can't redirect to pipe %s: %s\n", p, strerror(errno)); @@ -260,103 +223,48 @@ dopipe(char *p) } static void -doexecute(char *p, FILE *auxfi) +doexecute(char *p) { - int fd; - char *tag; - - tag = gettag(p); - if (tag == NULL) { + if (!seen_input(p)) { fprintf(stderr, - "WARNING! Server attempted unauthorized read of file %s\n", + "WARNING! Server attempted to read file %s", p); return; } - free(tag); p = fname(p); if (*p == 0) { fprintf(stderr, "Need a file to execute\n"); return; } - if ((fd = open(p, O_RDONLY, 0)) < 0) { + + if ((input_fd = open(p, O_RDONLY)) < 0) { fprintf(stderr, "Can't open execute file %s: %s\n", p, strerror(errno)); return; } - /* copies 4k at a time to the socket */ - while (termio(fd, sock, auxfi)) /*do copy */ - ; - /* - * Some platforms don't send the eof (cntl-D) at the end of - * copying a file. If emp_client hangs at the end of an - * execute, include the following line and notify wolfpack - * of the platform you are using. - * sendeof(sock); - */ - close(fd); } -static void -output(int code, char *buf, FILE *auxfi) +void +outch(char c) { - switch (code) { - case C_NOECHO: - /* not implemented; server doesn't send it */ - break; - case C_ABORT: - printf("Aborted\n"); - if (auxfi) - fprintf(auxfi, "Aborted\n"); - break; - case C_CMDERR: - case C_BADCMD: - printf("Error; "); - if (auxfi) - fprintf(auxfi, "Error; "); - break; - case C_EXIT: - printf("Exit: "); - if (auxfi) - fprintf(auxfi, "Exit: "); - break; - case C_FLASH: - printf("\n"); - break; - default: - break; - } - if (auxfi) { - fprintf(auxfi, "%s", buf); - } - + if (auxfp) + putc(c, auxfp); if (redir_fp) - fprintf(redir_fp, "%s", buf); + putc(c, redir_fp); else if (pipe_fp) - fprintf(pipe_fp, "%s", buf); - else { - screen(buf); - } -} - -static void -screen(char *buf) -{ - char c; - - while ((c = *buf++)) { - if (eight_bit_clean) { - if (c == 14) - putso(); - else if (c == 15) - putse(); - else - putchar(c); - } else if (c & 0x80) { + putc(c, pipe_fp); + else if (eight_bit_clean) { + if (c == 14) putso(); - putchar(c & 0x7f); + else if (c == 15) putse(); - } else + else putchar(c); - } + } else if (c & 0x80) { + putso(); + putchar(c & 0x7f); + putse(); + } else + putchar(c); } diff --git a/src/client/serverio.c b/src/client/serverio.c deleted file mode 100644 index 7df0ae10a..000000000 --- a/src/client/serverio.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * serverio.c: Handle input from server - * - * Known contributors to this file: - * Steve McClure, 1998 - */ - -#include - -#include -#include -#if !defined(_WIN32) -#include -#endif -#include "misc.h" -#include "ioqueue.h" - -int -serverio(int s, struct ioqueue *ioq) -{ - char *buf; - int n; - - if ((buf = malloc(ioq->bsize)) == NULL) { - fprintf(stderr, "malloc server i/o failed\n"); - return 0; - } -#ifdef _WIN32 - n = recv(s, buf, ioq->bsize, 0); -#else - n = read(s, buf, ioq->bsize); -#endif - if (n < 0) { -#ifdef _WIN32 - errno = WSAGetLastError(); -#endif - perror("server i/o read"); - free(buf); - return 0; - } - if (n == 0) { - fprintf(stderr, "Server EOF\n"); -#ifdef WIN32 - (void)closesocket(s); -#else - (void)close(s); -#endif - return 0; - } - if (n != ioq->bsize) - buf = realloc(buf, n); - ioq_write(ioq, buf, n); - return 1; -} diff --git a/src/client/tags.c b/src/client/tags.c deleted file mode 100644 index 971391022..000000000 --- a/src/client/tags.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * tags.c: save redir, execute and pipe info to protect against tampering - * by the deity. - * - * Known contributors to this file: - * John Yockey, 1998 - */ - -#include - -#include -#include -#include "misc.h" -#include "tags.h" - -struct tagstruct *taglist; - -void -io_init(void) -{ - taglist = NULL; -} - -char * -gettag(char *p) -{ - struct tagstruct *tag1, *tag2; - - if (taglist == NULL) - return NULL; - tag1 = taglist; - if (!strncmp(tag1->item, p, strlen(tag1->item))) { - p = tag1->item; - taglist = taglist->next; - free(tag1); - return p; - } - while (tag1->next != NULL) { - tag2 = tag1->next; - if (!strncmp(tag2->item, p, strlen(tag2->item))) { - p = tag2->item; - tag1->next = tag2->next; - free(tag2); - return p; - } - tag1 = tag1->next; - } - return NULL; -} diff --git a/src/client/termio.c b/src/client/termio.c deleted file mode 100644 index 9819bde0a..000000000 --- a/src/client/termio.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Empire - A multi-player, client/server Internet based war game. - * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak, - * Ken Stevens, Steve McClure - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * --- - * - * See files README, COPYING and CREDITS in the root of the source - * tree for related information and legal notices. It is expected - * that future projects/authors will amend these files as needed. - * - * --- - * - * termio.c: Various io functions - * - * Known contributors to this file: - * Steve McClure, 1998 - */ - -#include - -#include -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif -#include "misc.h" -#include "tags.h" - -static int sendeof(int sock); - -int -termio(int fd, int sock, FILE *auxfi) -{ - static char exec[] = "execute"; - static char buf[4096]; - char out[4096]; - int i, n; - char *ptr; - char *p, *q, *r, *s, *t; - int nbytes; - int prespace, exec_com, inarg, quoted, tagging; - struct tagstruct *tag; -#ifdef _WIN32 - char c; - INPUT_RECORD InpBuffer[2]; - int ret; - DWORD records; -#endif - - i = strlen(buf); - p = buf + i; -#ifndef _WIN32 - n = read(fd, p, sizeof(buf) - i); -#else -/* The keyboard is sometimes generating both keydown and keyup - * events for the same key. Thus, we only want to grab keydown - * events. */ - if (fd == -1) { - ret = PeekConsoleInput(hStdIn, InpBuffer, 1, &records); - if (!ret) { - fprintf(stderr, "Error peeking the console input (%lu)\n", - GetLastError()); - return 0; - } - if (records == 0) - return 0; - if (InpBuffer[0].EventType != KEY_EVENT) { - ret = ReadConsoleInput(hStdIn, InpBuffer, 1, &records); - if (!ret) { - fprintf(stderr, "Error reading the console input (%lu)\n", - GetLastError()); - return 0; - } - if (records == 0) - return 0; - return 1; - } - if (!InpBuffer[0].Event.KeyEvent.bKeyDown) { - ret = ReadConsoleInput(hStdIn, InpBuffer, 1, &records); - if (!ret) { - fprintf(stderr, "Error reading the console input (%lu)\n", - GetLastError()); - return 0; - } - if (records == 0) - return 0; - return 1; - } - c = InpBuffer[0].Event.KeyEvent.uChar.AsciiChar; - - if (c == 13) - c = 10; - records = 1; - p[0] = c; - p[1] = '\0'; - if (c != 10) { - ret = ReadConsole(hStdIn, p, sizeof(buf) - i, &records, NULL); - if (!ret) { - fprintf(stderr, "Error reading the console (%lu)\n", - GetLastError()); - return 0; - } - } else - putchar(c); -/* Strip off the CRLF to just LF */ - if (records > 1) { - if (p[records - 2] == 13 && p[records - 1] == 10) { - p[records - 2] = 10; - p[records - 1] = 0; - records--; - } - } - FlushConsoleInputBuffer(hStdIn); - if (records == 0) - return 0; - n = records; - } else if (fd == 0) { - if (feof(stdin)) { - sendeof(sock); - return 0; - } - fgets(p, sizeof(buf) - i, stdin); - n = strlen(p); - } else { - n = read(fd, p, sizeof(buf) - i); - } -#endif - if (n == 0) { - sendeof(sock); - return 0; - } - if (n < 0) { - perror("read standard input"); - return 0; - } - n += i; - ptr = buf; - p = buf; - q = out; - r = out; - tagging = 0; - inarg = 0; - prespace = 1; - quoted = 0; - exec_com = 0; - while (p < buf + n && q < out + 4000) { - if (*p == '\n') { - if (tagging) { - tag = malloc(sizeof(struct tagstruct)); - tag->item = malloc(1 + p - s); - tag->next = taglist; - taglist = tag; - t = tag->item; - while (s < p) - *t++ = *s++; - *t = 0; - } - *q++ = *p++; - tagging = 0; - inarg = 0; - prespace = 1; - quoted = 0; - exec_com = 0; - ptr = p; - r = q; - } else if (tagging) { - *q++ = *p++; - } else if (!quoted && isspace(*p)) { - *q++ = *p++; - prespace = 1; - if (exec_com && s > exec + 2) { - tagging = 1; - s = p; - } - } else if (prespace && *p == '|') { - tagging = 1; - *q++ = *p++; - s = p; - } else if (prespace && *p == '>') { - tagging = 1; - *q++ = *p++; - if (*p != '\n' && (*p == '!' || *p == '>')) - *q++ = *p++; - s = p; - } else { - prespace = 0; - if (*p == '"') { - quoted = !quoted; - } else { - if (!inarg && *p != '?') { - s = exec; - exec_com = 1; - inarg = 1; - } - if (exec_com && *s && *s++ != *p) - exec_com = 0; - } - *q++ = *p++; - } - } - p = buf; - while (ptr < buf + n) - *p++ = *ptr++; - *p = 0; - ptr = out; - n = r - out; - if (auxfi) { - fwrite(out, n, 1, auxfi); - } - while (n > 0) { -#ifndef _WIN32 - nbytes = write(sock, ptr, n); -#else - nbytes = send(sock, ptr, n, 0); -#endif - if (nbytes <= 0) { -#ifdef _WIN32 - errno = WSAGetLastError(); -#endif - perror("write server socket"); - return 0; - } - ptr += nbytes; - n -= nbytes; - } - return 1; -} - -static int -sendeof(int sock) -{ -#ifndef _WIN32 - if (write(sock, "ctld\n", 5) < 5) { -#else - if (send(sock, "ctld\n", 5, 0) < 5) { -#endif - fprintf(stderr, "sendeof: EOF send failed\n"); -#ifdef _WIN32 - closesocket(sock); -#else - close(sock); -#endif - return 0; - } - return 1; -} -- 2.43.0