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.
This commit is contained in:
Markus Armbruster 2007-11-17 14:17:38 +00:00
parent 2444a5c63a
commit 8b7d0b915d
15 changed files with 862 additions and 1127 deletions

View file

@ -53,8 +53,8 @@ srcdir = @srcdir@
VPATH = @srcdir@ VPATH = @srcdir@
prog = empire$E prog = empire$E
obj = expect.$O host.$O ioqueue.$O ipglob.$O login.$O main.$O queue.$O \ obj = expect.$O host.$O ipglob.$O linebuf.$O login.$O main.$O play.$O \
servcmd.$O serverio.$O tags.$O termio.$O termlib.$O version.$O ringbuf.$O secure.$O servcmd.$O termlib.$O version.$O
all: $(prog) all: $(prog)
@ -81,14 +81,13 @@ uninstall:
# FIXME generate from .d # FIXME generate from .d
expect.$O: misc.h expect.$O: misc.h
host.$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 login.$O: misc.h proto.h
main.$O: misc.h proto.h queue.h ioqueue.h tags.h version.h main.$O: misc.h version.h
queue.$O: misc.h queue.h play.$O: linebuf.h misc.h proto.h ringbuf.h secure.h
servcmd.$O: misc.h proto.h queue.h ioqueue.h tags.h ringbuf.$O: ringbuf.h
serverio.$O: misc.h queue.h ioqueue.h secure.$O: ringbuf.h secure.h
tags.$O: misc.h tags.h servcmd.$O: misc.h proto.h secure.h
termio.$O: misc.h tags.h
termlib.$O: misc.h termlib.$O: misc.h
version.$O: version.h version.$O: version.h
$(obj): config.h $(obj): config.h

View file

@ -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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

107
src/client/linebuf.c Normal file
View file

@ -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 <config.h>
#include <assert.h>
#include <stdlib.h>
#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;
}

View file

@ -25,24 +25,28 @@
* *
* --- * ---
* *
* queue.h: generic vax-like doubly linked list queues. * linebuf.h: Simple line buffer
* *
* Known contributors to this file: * Known contributors to this file:
* * Markus Armbruster, 2007
*/ */
#ifndef QUEUE_H #ifndef LINEBUF_H
#define QUEUE_H #define LINEBUF_H
#define QEMPTY(p) ((p)->q_forw == (p)) #define LBUF_LEN_MAX 4096
struct qelem { struct lbuf {
struct qelem *q_forw; /* All members are private! */
struct qelem *q_back; 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); extern void lbuf_init(struct lbuf *);
void remque(struct qelem *p); extern int lbuf_len(struct lbuf *);
void initque(struct qelem *p); extern int lbuf_full(struct lbuf *);
extern char *lbuf_line(struct lbuf *);
extern int lbuf_putc(struct lbuf *, char);
#endif #endif

View file

@ -36,39 +36,12 @@
#include <config.h> #include <config.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <io.h>
#else
#include <pwd.h> #include <pwd.h>
#include <sys/time.h> #include <stdlib.h>
#include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#endif
#include "ioqueue.h"
#include "misc.h" #include "misc.h"
#include "proto.h"
#include "tags.h"
#include "version.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 static void
print_usage(char *program_name) print_usage(char *program_name)
{ {
@ -94,25 +67,10 @@ main(int argc, char **argv)
char *uname; char *uname;
char *host; char *host;
char *port; char *port;
int sock;
#ifdef _WIN32 #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]; char unamebuf[128];
#else
struct sigaction sa;
fd_set mask;
fd_set savemask;
int retry = 0;
#endif #endif
struct ioqueue server;
FILE *auxout_fp = NULL;
int n;
#ifdef _WIN32 #ifdef _WIN32
/* /*
@ -124,9 +82,6 @@ main(int argc, char **argv)
* after each prompt is required. * after each prompt is required.
*/ */
setvbuf(stdout, NULL, _IOLBF, 4096); setvbuf(stdout, NULL, _IOLBF, 4096);
#else
FD_ZERO(&mask);
FD_ZERO(&savemask);
#endif #endif
while ((opt = getopt(argc, argv, "2:kuhv")) != EOF) { while ((opt = getopt(argc, argv, "2:kuhv")) != EOF) {
@ -192,14 +147,13 @@ main(int argc, char **argv)
} }
getsose(); 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); fprintf(stderr, "Unable to open %s for append\n", auxfname);
exit(1); exit(1);
} }
#ifdef _WIN32 #ifdef _WIN32
wVersionRequested = MAKEWORD(2, 0); err = WSAStartup(MAKEWORD(2, 0), &WsaData);
err = WSAStartup(wVersionRequested, &WsaData);
if (err != 0) { if (err != 0) {
printf("WSAStartup Failed, error code %d\n", err); printf("WSAStartup Failed, error code %d\n", err);
exit(1); exit(1);
@ -211,156 +165,8 @@ main(int argc, char **argv)
if (!login(sock, uname, country, passwd, send_kill, utf8)) if (!login(sock, uname, country, passwd, send_kill, utf8))
exit(1); exit(1);
ioq_init(&server, 2048); if (play(sock) < 0)
io_init(); exit(1);
#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
}
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; return 0;
interrupt = 0;
}
return 1;
} }

View file

@ -39,19 +39,16 @@
#include <windows.h> #include <windows.h>
#endif #endif
struct ioqueue; #define MAX(a, b) ((a) >= (b) ? (a) : (b))
extern char empirehost[]; extern char empirehost[];
extern char empireport[]; extern char empireport[];
extern int eight_bit_clean; extern int eight_bit_clean;
extern int sock; extern int input_fd;
extern FILE *auxfp;
extern char *SO; extern char *SO;
extern char *SE; extern char *SE;
#ifdef _WIN32
extern HANDLE hStdIn;
#endif
#ifdef _WIN32 #ifdef _WIN32
#define getsose() ((void)0) #define getsose() ((void)0)
#define putso() ((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 expect(int s, int match, char *buf);
int tcp_connect(char *, char *); int tcp_connect(char *, char *);
int login(int s, char *uname, char *cname, char *cpass, int kill_proc, int); 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 sendcmd(int s, char *cmd, char *arg);
void servercmd(struct ioqueue *ioq, FILE *auxfi); void servercmd(int, char *, int);
int serverio(int s, struct ioqueue *ioq); void outch(char);
int termio(int fd, int sock, FILE *auxfi);
#endif #endif

316
src/client/play.c Normal file
View file

@ -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 <config.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#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;
}
}
}
}

245
src/client/ringbuf.c Normal file
View file

@ -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 <config.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#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;
}

View file

@ -25,38 +25,43 @@
* *
* --- * ---
* *
* ioqueue.h: stores and frees data associated with a file descriptor. * ringbuf.h: Simple ring buffer
* uses writev to write, and read to read.
* *
* Known contributors to this file: * Known contributors to this file:
* * Markus Armbruster, 2007
*/ */
#ifndef IOQUEUE_H #ifndef RINGBUF_H
#define IOQUEUE_H #define RINGBUF_H
#include "queue.h" #include <stddef.h>
struct ioqueue { #define RING_SIZE 4096
struct qelem queue; /* queue fwd/back */
int bsize; /* basic block size */ /* Ring buffer, consumer reads, producer writes */
int cc; /* character count */ 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];
}; };
struct io { extern void ring_init(struct ring *);
struct qelem queue; /* list of ioqueue elements */ extern int ring_len(struct ring *);
int nbytes; /* number of data bytes present */ extern int ring_space(struct ring *);
int offset; /* offset into current entry */ extern int ring_peek(struct ring *, int);
char *data; /* pointer to start */ extern int ring_getc(struct ring *);
}; extern int ring_putc(struct ring *, unsigned char);
extern int ring_putm(struct ring *, void *, size_t);
void ioq_init(struct ioqueue *ioq, int bsize); extern void ring_discard(struct ring *, int);
int ioq_peek(struct ioqueue *ioq, char *buf, int cc); extern int ring_search(struct ring *, char *);
int ioq_dequeue(struct ioqueue *ioq, int cc); extern int ring_from_file(struct ring *, int fd);
int ioq_read(struct ioqueue *ioq, char *buf, int cc); extern int ring_to_file(struct ring *, int fd);
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);
#endif #endif

View file

@ -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: * Known contributors to this file:
* Steve McClure, 1998 * Markus Armbruster, 2007
*/ */
#include <config.h> #include <config.h>
#include "queue.h" #include <string.h>
#include "ringbuf.h"
#include "secure.h"
struct ring recent_input;
void void
insque(struct qelem *p, struct qelem *q) save_input(char *inp)
{ {
p->q_forw = q->q_forw; size_t len = strlen(inp);
p->q_back = q; int left;
q->q_forw->q_back = p;
q->q_forw = p; 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 int
remque(struct qelem *p) seen_input(char *tail)
{ {
p->q_back->q_forw = p->q_forw; int dist = ring_search(&recent_input, tail);
p->q_forw->q_back = p->q_back;
}
void if (dist < 0)
initque(struct qelem *p) return 0;
{
p->q_forw = p; ring_discard(&recent_input, dist + strlen(tail));
p->q_back = p; return 1;
} }

View file

@ -25,23 +25,17 @@
* *
* --- * ---
* *
* tags.h: Tag structure definition * secure.h: Check redir etc. to protect against tampering deity
* *
* Known contributors to this file: * Known contributors to this file:
* John Yockey, 1998 * Markus Armbruster, 2007
*/ */
#ifndef TAGS_H #ifndef SECURE_H
#define TAGS_H #define SECURE_H
struct tagstruct { extern void clear_recent_input(void);
char *item; extern void save_input(char *);
struct tagstruct *next; extern int seen_input(char *);
};
extern struct tagstruct *taglist;
void io_init(void);
char *gettag(char *p);
#endif #endif

View file

@ -35,119 +35,88 @@
#include <config.h> #include <config.h>
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include "ioqueue.h"
#include "misc.h" #include "misc.h"
#include "proto.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 *redir_fp;
static FILE *pipe_fp; static FILE *pipe_fp;
static void prompt(FILE *auxfi); static void prompt(int, char *, char *);
static void doredir(char *p); static void doredir(char *p);
static void dopipe(char *p); static void dopipe(char *p);
static void doexecute(char *p, FILE *auxfi); static void doexecute(char *p);
static void output(int code, char *buf, FILE *auxfi);
static void screen(char *buf);
void void
servercmd(struct ioqueue *ioq, FILE *auxfi) servercmd(int code, char *arg, int len)
{ {
char buf[1024]; static int nmin, nbtu;
char *p; static char the_prompt[1024];
static int code = -1; static char teles[64];
int eol;
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;
}
/*
* FIXME
* C_REDIR, C_PIPE, and C_EXECUTE will not
* work with filename longer than one buffer
*/
switch (code) { switch (code) {
case C_PROMPT: case C_PROMPT:
if (sscanf(p, "%d %d", &nmin, &nbtu) != 2) { if (sscanf(arg, "%d %d", &nmin, &nbtu) != 2) {
fprintf(stderr, "prompt: bad server prompt %s\n", p); fprintf(stderr, "prompt: bad server prompt %s\n", arg);
} }
mode = code; snprintf(the_prompt, sizeof(the_prompt), "[%d:%d] Command : ",
sprintf(the_prompt, "[%d:%d] Command : ", nmin, nbtu); nmin, nbtu);
prompt(auxfi); prompt(code, the_prompt, teles);
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; break;
case C_FLUSH: case C_FLUSH:
mode = code; snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg);
if (eol) prompt(code, the_prompt, teles);
p[strlen(p) - 1] = '\0';
sprintf(the_prompt, "%s", p);
prompt(auxfi);
break; break;
case C_EXECUTE: case C_EXECUTE:
if (eol) doexecute(arg);
p[strlen(p) - 1] = '\0'; break;
doexecute(p, auxfi); 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; break;
case C_INFORM: case C_INFORM:
if (eol) if (*arg) {
p[strlen(p) - 1] = '\0'; snprintf(teles, sizeof(teles), "(%.*s )", len -1, arg);
if (*p) {
p[strlen(p) - 1] = '\0';
sprintf(num_teles, "(%s) ", p + 1);
if (!redir_fp && !pipe_fp) { if (!redir_fp && !pipe_fp) {
putchar('\07'); putchar('\07');
prompt(NULL); prompt(code, the_prompt, teles);
} }
} else } else
*num_teles = '\0'; teles[0] = 0;
break;
case C_PIPE:
dopipe(arg);
break;
case C_REDIR:
doredir(arg);
break; break;
default: default:
output(code, p, auxfi); assert(0);
break; break;
} }
if (eol)
code = -1;
}
} }
static void 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) { if (redir_fp) {
(void)fclose(redir_fp); (void)fclose(redir_fp);
redir_fp = NULL; redir_fp = NULL;
@ -156,13 +125,12 @@ prompt(FILE *auxfi)
pipe_fp = NULL; pipe_fp = NULL;
} }
} }
if (mode == C_PROMPT) nl = code == C_PROMPT || code == C_INFORM ? "\n" : "";
printf("\n"); printf("%s%s%s", nl, teles, prompt);
printf("%s%s", num_teles, the_prompt); fflush(stdout);
(void)fflush(stdout); if (auxfp) {
if (auxfi) { fprintf(auxfp, "%s%s%s", nl, teles, prompt);
fprintf(auxfi, "\n%s%s", num_teles, the_prompt); fflush(auxfp);
(void)fflush(auxfi);
} }
} }
@ -183,7 +151,6 @@ fname(char *s)
static void static void
doredir(char *p) doredir(char *p)
{ {
char *tag;
int mode; int mode;
int fd; int fd;
@ -192,6 +159,12 @@ doredir(char *p)
redir_fp = NULL; redir_fp = NULL;
} }
if (!seen_input(p)) {
fprintf(stderr, "WARNING! Server attempted to redirect %s\n",
p);
return;
}
if (*p++ != '>') { if (*p++ != '>') {
fprintf(stderr, "WARNING! Weird redirection %s", p); fprintf(stderr, "WARNING! Weird redirection %s", p);
return; return;
@ -207,19 +180,12 @@ doredir(char *p)
} else } else
mode |= O_EXCL; mode |= O_EXCL;
tag = gettag(p);
p = fname(p); p = fname(p);
if (tag == NULL) {
fprintf(stderr, "WARNING! Server redirected output to file %s\n",
p);
return;
}
free(tag);
if (*p == 0) { if (*p == 0) {
fprintf(stderr, "Redirection lacks a file name\n"); fprintf(stderr, "Redirection lacks a file name\n");
return; return;
} }
fd = open(p, mode, 0600); fd = open(p, mode, 0600);
redir_fp = fd < 0 ? NULL : fdopen(fd, "w"); redir_fp = fd < 0 ? NULL : fdopen(fd, "w");
if (!redir_fp) { if (!redir_fp) {
@ -234,25 +200,22 @@ doredir(char *p)
static void static void
dopipe(char *p) dopipe(char *p)
{ {
char *tag; if (!seen_input(p)) {
fprintf(stderr, "WARNING! Server attempted to pipe %s", p);
return;
}
if (*p++ != '|') { if (*p++ != '|') {
fprintf(stderr, "WARNING! Weird pipe %s", p); fprintf(stderr, "WARNING! Weird pipe %s", p);
return; return;
} }
tag = gettag(p);
if (tag == NULL) {
fprintf(stderr, "WARNING! Server attempted to run: %s\n", p);
return;
}
free(tag);
for (; *p && isspace(*p); p++) ; for (; *p && isspace(*p); p++) ;
if (*p == 0) { if (*p == 0) {
fprintf(stderr, "Redirection lacks a command\n"); fprintf(stderr, "Redirection lacks a command\n");
return; return;
} }
if ((pipe_fp = popen(p, "w")) == NULL) { if ((pipe_fp = popen(p, "w")) == NULL) {
fprintf(stderr, "Can't redirect to pipe %s: %s\n", fprintf(stderr, "Can't redirect to pipe %s: %s\n",
p, strerror(errno)); p, strerror(errno));
@ -260,92 +223,38 @@ dopipe(char *p)
} }
static void static void
doexecute(char *p, FILE *auxfi) doexecute(char *p)
{ {
int fd; if (!seen_input(p)) {
char *tag;
tag = gettag(p);
if (tag == NULL) {
fprintf(stderr, fprintf(stderr,
"WARNING! Server attempted unauthorized read of file %s\n", "WARNING! Server attempted to read file %s",
p); p);
return; return;
} }
free(tag);
p = fname(p); p = fname(p);
if (*p == 0) { if (*p == 0) {
fprintf(stderr, "Need a file to execute\n"); fprintf(stderr, "Need a file to execute\n");
return; 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", fprintf(stderr, "Can't open execute file %s: %s\n",
p, strerror(errno)); p, strerror(errno));
return; 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 void
output(int code, char *buf, FILE *auxfi) outch(char c)
{ {
switch (code) { if (auxfp)
case C_NOECHO: putc(c, auxfp);
/* 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 (redir_fp) if (redir_fp)
fprintf(redir_fp, "%s", buf); putc(c, redir_fp);
else if (pipe_fp) else if (pipe_fp)
fprintf(pipe_fp, "%s", buf); putc(c, pipe_fp);
else { else if (eight_bit_clean) {
screen(buf);
}
}
static void
screen(char *buf)
{
char c;
while ((c = *buf++)) {
if (eight_bit_clean) {
if (c == 14) if (c == 14)
putso(); putso();
else if (c == 15) else if (c == 15)
@ -358,5 +267,4 @@ screen(char *buf)
putse(); putse();
} else } else
putchar(c); putchar(c);
}
} }

View file

@ -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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#if !defined(_WIN32)
#include <unistd.h>
#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;
}

View file

@ -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 <config.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View file

@ -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 <config.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#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;
}