#include "security.h"
#define TELOPTS
#include <arpa/telnet.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#include "proto.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef aix
#include <sys/select.h>
#endif
#include "comm.h"
#include "extern_prog.h"
#include "dgram.h"
#include "consts.h"
#include "gd_config.h"
#include "fsecure.h"
#define INET_NTOA_OK
#define SNOOP_SUPPORT
// Extra telnet neg stuff
#ifndef TELOPT_COMPRESS
#define TELOPT_COMPRESS 85
#define TELOPT_COMPRESS2 86
#endif
#ifdef HAVE_ZLIB_H
void end_compression(struct interactive * ip);
void start_compression(struct interactive * ip);
int flush_compressed_output(struct interactive * ip);
int write_compressed(struct interactive * ip, char * data, int length);
#endif
int needs_tm = 0;
static int telnet_neg(struct interactive *, char *, char *, char *, int);
static char * first_cmd_in_buf(struct interactive *);
static void next_cmd_in_buf(struct interactive *);
void new_player(int, int, struct sockaddr_in *, int);
void flush_output(struct interactive *, int);
void make_nonblocking(int socket);
struct interactive *all_players[MAX_PLAYERS];
fd_set readfds;
int nfds = 0;
int num_player;
/*
* Interprocess communication interface to the backend.
*/
static int s = 0;
int dgram_sock = 0;
extern int port_number;
extern int e_port_number;
static int s2 = 0;
static int e_s2 = 0;
extern int port_number2;
extern int e_port_number2;
void make_nonblocking(int socket)
{
#if 0
// FIX: should be determined by configure.
if (ioctl(s, FIONBIO, &tmp) == -1) {
perror("ioctl socket FIONBIO");
abort();
}
#else /* sun */
if (fcntl(socket, F_SETFL, FNDELAY) == -1) {
perror("fcntl socket F_SETFL O_NDELAY");
return;
}
#endif /* sun */
}
int mk_bnd_sock(int ty, int proto, int port_number)
{
struct sockaddr_in sin;
struct hostent *hp;
int tmp;
char host_name[100];
int s;
if (gethostname(host_name, sizeof host_name) == -1)
{
perror("gethostname");
fatal("Error in gethostname()\n");
}
hp = gethostbyname(host_name);
if (hp == 0)
{
(void) perror("gethostbyname");
exit(1);
}
memset((char *) &sin, '\0', sizeof sin);
memcpy((char *) &sin.sin_addr, hp->h_addr, hp->h_length);
sin.sin_port = htons(port_number);
sin.sin_family = hp->h_addrtype;
sin.sin_addr.s_addr = INADDR_ANY;
s = socket(AF_INET, ty, 0);
if (s == -1) {
perror("socket");
return -1;
}
tmp = 1;
#if 1
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *) &tmp, sizeof(tmp)) < 0) {
perror("setsockopt");
exit(1);
}
#endif
if (bind(s, (struct sockaddr *) & sin, sizeof sin) == -1) {
if (errno == EADDRINUSE) {
debug_message("Socket already bound!\n");
exit(errno);
} else {
perror("bind");
return -1;
}
}
tmp = 1;
make_nonblocking(s);
signal(SIGPIPE, SIG_IGN);
return s;
}
void
add_output_str(struct interactive * ip, int filter_eol, char * buff)
{
Shared * nbuff;
nbuff = string_copy(buff);
add_output(ip, filter_eol, nbuff);
free_string(nbuff);
}
#define white_space(x) (x == ' ' || x == '\n' || x == '\t')
void
add_output(struct interactive * ip, int filter_eol, Shared * buff)
{
int length;
int from, to, outsize;
char prevc = 0;
char * buff2;
unsigned char alloced = 0;
if (!ip) return;
if (ip->my_z_stream == NULL)
{
length = buff->length;
outsize = buff->length * 2 + 10;
buff2 = (char *)malloc(outsize * sizeof(char));
if (!buff2)
{
debug_message("add_output: Unable to allocate %d byte!\n", outsize);
return;
}
else
{
alloced = 1;
}
for (from = 0, to = 0; to < outsize && from < length;)
{
if (buff->str[from] == '\n' && prevc != '\r') buff2[to++] = '\r';
/* keep the same */
prevc = buff2[to++] = buff->str[from++];
}
// too long a message - truncate ruthlessly (FIX: need better buffering)
if (to == outsize) buff2[to-1] = '\0';
else buff2[to++] = '\0';
length = to - 1;
}
else
{
buff2 = buff->str;
length = buff->length;
}
binary_output(ip, buff2, length);
if (alloced) free(buff2);
}
void
binary_output(struct interactive * ip, unsigned char * buff, unsigned int length)
{
int n, offset, chunk;
if (!ip) return;
/*
* We split up the message into something smaller than the max size.
*/
for (offset = 0; length > 0; offset += chunk, length -= chunk)
{
chunk = length;
if (chunk > MAX_SOCKET_PACKET_SIZE)
chunk = MAX_SOCKET_PACKET_SIZE;
#ifdef HAVE_ZLIB_H
if (ip->my_z_stream != NULL)
{
n = write_compressed(ip, buff + offset, chunk);
if (n == 0) n = -1;
}
else
#endif
{
n = write(ip->socket, buff + offset, chunk);
}
if (n == -1)
{
if (errno == EMSGSIZE)
{
debug_message("comm1: write EMSGSIZE.\n");
return;
}
else if (errno == EINVAL)
{
debug_message("comm1: write EINVAL.\n");
}
else if (errno == ENETUNREACH)
{
debug_message("comm1: write ENETUNREACH.\n");
}
else if (errno == EHOSTUNREACH)
{
debug_message("comm1: write EHOSTUNREACH.\n");
}
else if (errno == EPIPE)
{
debug_message("comm1: write EPIPE detected\n");
}
else if (errno == EWOULDBLOCK || errno == EAGAIN)
{
debug_message("comm1: write EWOULDBLOCK. Message discarded.\n");
// if (!ip->closing) remove_interactive(ip->ob);
return;
}
else
{
debug_message("write: unknown errno %d\n", (char *)errno);
}
perror("write");
if (!ip->closing) remove_interactive(ip->ob);
return;
}
if (n != chunk)
{
debug_message("write socket: Size %d(%d) is to big !\n",
chunk, n);
}
chunk = n;
continue;
}
// FIX: should implement some buffering so we don't
// discard stuff on a too long chunk or on an EAGAIN
}
/*
* do all I/O updating - transfer socket input to players' command buffers,
* take external program input and output and transfer it to the appropriate
* place, do accepts on external program sockets, and handle the external
* program state machine. In addition take output from external programs, and
* strip the MUD commands, placing them in the command buffer
*/
int twait = 0; /* wait time for select() */
int update_io(int quick)
{
int i, res;
int coms; /* ms till next call_out */
struct interactive *ip = 0;
int new_socket;
struct sockaddr_in addr;
int length;
struct timeval timeout;
char buff[MAX_TEXT_OUT];
/* check if I/O is open yet */
if (s == 0) return 1;
/* First, try to get a new player... */
length = sizeof addr;
new_socket = accept(s, (struct sockaddr *) &addr, &length);
if (new_socket != -1)
{
make_nonblocking(new_socket);
new_player(0, new_socket, &addr, length);
}
else if (new_socket == -1 && errno != EWOULDBLOCK && errno != EINTR)
{
perror("accept1");
/* abort(); */
}
new_socket = accept(s2, (struct sockaddr *) &addr, &length);
if (new_socket != -1)
{
make_nonblocking(new_socket);
new_player(1, new_socket, &addr, length);
}
else if (new_socket == -1 && errno != EWOULDBLOCK && errno != EINTR)
{
perror("accept2");
/* abort(); */
}
nfds = 0;
FD_ZERO(&readfds);
for (i = 0; i < MAX_PLAYERS; i++)
{
ip = all_players[i];
/*
* next try to connect the player to an external program if necessary
*/
if (ip)
{
/* this player has created an external socket but not accepted */
if (ip->extern_socket == 0 && ip->my_ext_socket != 0)
{
int OtherSocketIdLen;
OtherSocketIdLen = sizeof(ip->extern_addr);
ip->extern_socket = accept(ip->my_ext_socket, &ip->extern_addr, &OtherSocketIdLen);
if (ip->extern_socket == -1 && errno != EWOULDBLOCK && errno != EINTR) { /* cancel the connect */
ip->extern_socket = 0;
kill_external(ip);
perror("External program socket accept failure");
debug_message("Accept failed starting external program\n\n");
return 0;
} else {
if (ip->extern_socket == -1)
ip->extern_socket = 0; /* try again later */
}
}
/* close this external program if it terminated */
if (ip->extern_status != -1)
{
/* extern program finished but not closed */
if (ip->extern_timeout >= EXTERN_TIMEOUT)
completed_external(ip); /* it's ages since the
* program finished so give
* up and close now */
else if (ip->extern_timeout >= 0)
ip->extern_timeout++; /* sockets haven't closed so
* wait some more */
else
completed_external(ip); /* child finished after
* socket closed - now finish */
}
else if (ip->extern_timeout < 0)
{
/* sockets closed waiting on child signal */
/* it's ages since the sockets closed so give up and kill it */
if (ip->extern_timeout <= -EXTERN_TIMEOUT)
kill_external(ip);
else
ip->extern_timeout--; /* program hasn't terminated
* after sockets closed so
* wait some more */
}
}
/* read from the input socket */
if (ip /* && !first_cmd_in_buf(ip) */)
{
FD_SET(ip->socket, &readfds);
if (ip->socket >= nfds)
nfds = ip->socket + 1;
if (ip->extern_socket != 0)
{
FD_SET(ip->extern_socket, &readfds);
if (ip->extern_socket >= nfds)
nfds = ip->extern_socket + 1;
}
#if 0
/* portals? You've got to be joking! */
if (ip->out_portal) {
FD_SET(ip->portal_socket, &readfds);
if (ip->portal_socket >= nfds)
nfds = ip->portal_socket + 1;
}
#endif
}
}
/* wait for the next input message */
update_current_time();
coms = first_call_time_us();
if (coms > MAX_CMD_TIME * 1000000 / 2) /* be sure we don't cause a shutdown due to `too long command response' */
coms = MAX_CMD_TIME * 1000000 / 2;
if (quick) {
timeout.tv_sec = 0; /* short delay only; 1/10 sec */
timeout.tv_usec = 100000;
} else {
if (twait * 1000000 < coms) {
timeout.tv_sec = twait; /* avoid busy waiting when no buffered cmds */
timeout.tv_usec = 0;
}
else {
timeout.tv_sec = coms / 1000000;
timeout.tv_usec = coms % 1000000;
}
}
FD_SET(dgram_sock, &readfds);
/* do_dg_read(dgram_sock); */
if (dgram_sock >= nfds) nfds = dgram_sock + 1;
res = select(nfds, &readfds, 0, 0, &timeout);
if (res == -1) {
twait = 0;
if (errno == EINTR) /* if we got an alarm, finish the round */
res = 0;
else {
perror("select");
return 0;
}
}
/* waiting packets */
if (res)
{
/* check the dgram socket */
if (FD_ISSET(dgram_sock, &readfds))
{
do_dg_read(dgram_sock);
}
/* read all pending sockets */
for (i = 0; i < MAX_PLAYERS; i++)
{
ip = all_players[i];
if (ip == 0) continue;
/* read this player */
if (FD_ISSET(ip->socket, &readfds))
{
int l;
/* how much space? Leave a byte for a null! */
l = MAX_TEXT_IN - ip->text_end - 1;
if (l < 0)
fatal("Overful input buffer??????");
/* no space! */
if (l == 0) continue;
if ((l = read(ip->socket, buff, l)) == -1)
{
if (errno == ENETUNREACH)
{
debug_message("*Net unreachable detected.\n");
remove_interactive(ip->ob);
}
else if (errno == EHOSTUNREACH)
{
debug_message("*Host unreachable detected.\n");
remove_interactive(ip->ob);
}
else if (errno == ETIMEDOUT)
{
debug_message("*Connection timed out detected.\n");
remove_interactive(ip->ob);
}
else if (errno == ECONNRESET)
{
debug_message("*Connection reset by peer detected.\n");
remove_interactive(ip->ob);
}
else if (errno == EWOULDBLOCK)
{
debug_message("*Read would block socket %d!\n",
(char *)ip->socket);
remove_interactive(ip->ob);
}
else if (errno == EMSGSIZE)
{
debug_message("*Read EMSGSIZE !\n");
}
else
{
debug_message("*Errno %d\n", (char *)errno);
}
perror("read");
continue;
//abort();
}
if (l == 0)
{
if (ip->closing)
fatal("*Tried to read from closing socket.\n");
remove_interactive(ip->ob);
continue;
}
buff[l] = '\0'; /* terminate it */
/* handle command input or external program input separately */
if (ip->extern_socket)
{
/* external program */
char ExtBuffer[MAX_SOCKET_PACKET_SIZE];
needs_tm = 0;
telnet_neg(ip, ExtBuffer, ExtBuffer, buff, 0);
send(ip->extern_socket, ExtBuffer, strlen(ExtBuffer), 0);
#ifdef SNOOP_SUPPORT
/* also send to any snoopers */
if (ip->snoop_by) {
add_output_str(ip->snoop_by, 0, "#");
add_output_str(ip->snoop_by, 0, ExtBuffer);
}
#endif
}
else
{
/* MUD command input */
needs_tm = 0;
ip->text_end += telnet_neg(ip, ip->text,
ip->text + ip->text_end, buff, 1);
if (needs_tm)
{
static unsigned char X[] = { (unsigned char) IAC,
(unsigned char) WILL, (unsigned char) TELOPT_TM, 0 };
add_output_str(ip, 0, X);
}
}
}
/* similarly check for external program output */
if (ip->extern_socket && FD_ISSET(ip->extern_socket, &readfds))
{
/* read from external program for output to player */
char Buffer[MAX_TEXT_OUT + 1];
int ActualSize;
ActualSize = recv(ip->extern_socket, Buffer, MAX_TEXT_OUT, 0);
if (ActualSize <= 0)
{
if (ActualSize == -1)
{
perror("external recv");
debug_message("Errno %d\n", (char *)errno);
kill_external(ip);
}
/* output stopped - program finished? */
if (ip->extern_status != -1)
completed_external(ip); /* already stopped - close
* now */
else
ip->extern_timeout = -1; /* close later */
}
else
{
Buffer[ActualSize] = 0;
extern_commands(ip->text + ip->text_end, Buffer); /* give commands to command handler */
ip->text_end += strlen(ip->text + ip->text_end);
add_output_str(ip, 0, Buffer);
}
}
}
}
child_termination(); /* allow the child another chance to terminate */
return 0;
}
#define StartCmdGiver (MAX_PLAYERS-1)
#define IncCmdGiver { NextCmdGiver = (NextCmdGiver < 0? StartCmdGiver: \
NextCmdGiver - 1); \
CmdsGiven = 0; }
int NextCmdGiver = StartCmdGiver;
int CmdsGiven = 0;
/* #include "dgram.c" */
/*
* Get a message from any player. For all players without a completed cmd in
* their input buffer, read their socket. Then, return the first cmd of the
* next player in sequence that has a complete cmd in their buffer. CmdsGiven
* is used to allow people in ED to send more cmds (if they have them queued
* up) than normal players. If we get a heartbeat, still read all sockets;
* if the next cmd giver is -1, we have already cycled and can go back to do
* the heart beat.
*/
int
get_message(char *buff, int size)
{
int i;
char *p = 0;
struct interactive *ip = 0;
if (size < MAX_TEXT_IN)
fatal("Stuffed it. size > MAX_TEXT_IN.");
/*
* Stay in this loop until we have a message from a player.
*/
while (1) {
/*
* read the input sockets, do external program I/O, etc.
*/
update_io(0);
/*
* we have read the sockets; now find and return a command
*/
twait = 0; /* assume we'll get one! */
ip = 0;
p = 0;
for (i = 0; i < MAX_PLAYERS; i++) {
int coms;
if (NextCmdGiver == -1) { /* we have cycled around all players */
IncCmdGiver;
update_current_time();
coms = first_call_time_us();
if (coms <= 0) /* check whether call_outs are ready */
return 0;
}
ip = all_players[NextCmdGiver];
if (ip)
{
if ( (p = first_cmd_in_buf(ip)) ) /* wont respond to partials */
break;
#if 0
else
{
/* should this really be here? */
flush_output(ip, 1);
}
#endif
}
IncCmdGiver;
}
if ((!ip) || !p)
{
/* no cmds found; loop and select (on timeout) again */
/* avoid busy wait on * select */
twait = 3;
/* 3 is nominal */
continue; /* else await another cmd */
}
/*
* we have a player cmd - return it. If he is in ed, count his cmds,
* else only allow 1 cmd. If he has only one partially completed cmd
* left after this, move it to the start of his buffer; new stuff
* will be appended.
*/
command_giver = ip->ob;
next_cmd_in_buf(ip); /* move buffer pointers, truncate p with null */
strcpy(buff, p); /* and suck up the command */
/*
* if he is not in ed, dont let him issue another till the poll comes
* again
*/
if (command_giver->interactive->ed_buffer
&& (CmdsGiven < ALLOWED_ED_CMDS))
CmdsGiven++;
else
IncCmdGiver;
#ifdef SNOOP_SUPPORT
/*
* manage snooping - should the snooper see type ahead? Well, he
* doesn't here
*/
if (ip->snoop_by && !ip->noecho) {
command_giver = ip->snoop_by->ob;
add_output_str(ip->snoop_by, 0, buff);
}
#endif
if (!ip->extern_pid) {
set_noecho(ip->ob, 0); /* checks to see that we're not in noecho */
}
command_giver = ip->ob;
return (1);
}
return 0;
}
/*
* find the first character of the next complete cmd in a buffer, 0 if no
* completed cmd. There is a completed cmd if there is a <cr> or <nl>
* between text_start and text_end. If there isn't, we move all the pending
* text to the start of the buffer so we get as much space as possible to
* append the next command. Also, if the buffer is completely full, we
* replace the last character by a newline and use that as the command.
*/
#define IsEol(c) ((c) == '\n' || (c) == '\r')
static char *
first_cmd_in_buf(struct interactive *ip)
{
char *p, *q;
if (ip->text_start >= ip->text_end) {
ip->text_start = ip->text_end = 0;
return (0);
}
p = ip->text_start + ip->text;
while ((p < (ip->text_end + ip->text)) && !IsEol(*p)) /* find end of cmd */
p++;
if (p < (ip->text + ip->text_end)) /* terminated, was command */
return (ip->text + ip->text_start);
/*
* have a partial command at end of buffer; move it to start. If,
* afterwards, we still have a full buffer, truncate it and return it as
* a command, else return null.
*/
if (ip->text_start > 0) {
p = ip->text + ip->text_start;
q = ip->text;
while (p < (ip->text + ip->text_end))
*(q++) = *(p++);
ip->text_end -= ip->text_start;
ip->text_start = 0;
}
if (ip->text_end >= MAX_TEXT_IN) { /* buffer completely full */
ip->text_end = MAX_TEXT_IN; /* shouldn't need this */
ip->text[MAX_TEXT_IN - 1] = '\n'; /* truncate it */
return (ip->text);
}
/* buffer not full and no newline - no cmd. */
return (0);
}
/*
* Move pointers to get to the next command in the buffer, if there is one.
* Truncate the previous command with a null, for telnet_neg (not done above
* because we don't send nulls through portals!).
*/
static void
next_cmd_in_buf(struct interactive *ip)
{
char *p = ip->text + ip->text_start;
char c;
while (p < ip->text + ip->text_end && !IsEol(*p))
p++;
/*
* Found end of current line; truncate line with null, if '\r\n' skip the
* extra char.
*/
if (p >= ip->text + ip->text_end)
fatal("Next cmd on ip with no next command!");
/* remember what it was */
c = (*p);
/* this replaces the newline - for telnet_neg */
*(p++) = '\0';
/* fix optional '\r\n' */
if ((c == '\r') && p < (ip->text + ip->text_end))
if (*p == '\n')
p++;
if (p < ip->text + ip->text_end)
{
ip->text_start = p - ip->text;
}
else
{
ip->text_start = ip->text_end = 0;
}
}
void
flush_output(struct interactive *ip, int p)
{
Obj * save = command_giver;
Val * v = 0;
if (!ip) return;
if (p) v = Const(1);
if (!ip->ob->destructed)
{
command_giver = ip->ob;
apply_clean(C("flush_output"), command_giver, v);
}
command_giver = save;
}
//
// Name: telnet_neg
// Purpose: handle the telnet protocol negotiation
//
// Horrible for windows clients (etc) which don't do line
// mode and come in character by character
//
// telnet state
#define TS_DATA 0
#define TS_IAC 1
#define TS_WILL 2
#define TS_WONT 3
#define TS_DO 4
#define TS_DONT 5
#define TS_SB 6
#define TS_SE 7
static int telnet_neg(struct interactive * ip,
char * cantback, char * to, char * from, int filter)
{
int state = TS_DATA;
int ch = 0;
char *first = to;
while (1)
{
ch = (*from++ & 0xff);
switch (state)
{
case TS_DATA:
if (ch == IAC)
{
state = TS_IAC;
continue;
}
// Mud input
if (filter)
{
/* Backspace / delete remove one character */
if (ch == '\b' || ch == 0x7f)
{
//printf("FOUND A BACKSPACE CHAR\n");
if (to <= cantback) continue;
*to-- = 0;
continue;
}
else if (ch == 0)
{
/* terminate */
*to = 0;
//printf("RETURNING: #%s#\n", first);
return to - first;
}
else if (ch < 32 && ch != '\t' && ch != '\n' && ch != '\r')
{
/* control codes */
if (0 && ch == 0x3)
{
/* terminate previous */
*to++ = 0;
/* save the ^C (mud only) */
*to++ = ch;
}
continue; /* ignore totally */
}
else
{
/* echo to input stream */
if (ch & 0x80)
{
/* debug_message("Tel_neg: 0x%x\n", ch); */
continue;
}
*to++ = ch;
continue;
}
}
else
{
// external program input
// echo to input stream. OK - so it's an ugly
// copy. Do I care? - Zik
// ignore control-C
if (ch == 0x03) continue;
// make cr/lf into just lf
if (ch == '\r' && *from == '\n') continue;
if (ch & 0x80)
{
/* debug_message("Tel_neg: 0x%x\n", ch); */
continue;
}
*to++ = ch;
if (ch == 0) return to - first - 1;
continue;
}
case TS_SE:
if (ch == SE)
{
// printf("RCVD TS_SE SE\n");
state = TS_DATA;
continue;
}
else if (ch == IAC)
{
// printf("RCVD TS_SE IAC\n");
state = TS_SB;
continue;
}
// fall through?
case TS_IAC:
switch (ch)
{
case WILL:
// printf("RCVD TS_IAC WILL ");
state = TS_WILL;
continue;
case WONT:
// printf("RCVD TS_IAC WONT ");
state = TS_WONT;
continue;
case DO:
// printf("RCVD TS_IAC DO ");
state = TS_DO;
continue;
case DONT:
// printf("RCVD TS_IAC DONT ");
state = TS_DONT;
continue;
case SB:
// printf("RCVD TS_IAC SB\n");
state = TS_SB;
continue;
case AYT:
// printf("RCVD TS_IAC AYT\n");
add_message("\n[Yes]\n");
continue;
case IP:
// printf("RCVD TS_IAC IP\n");
if (filter) // FIX: what? && 0)
{
/* mud only */
*to++ = 0; /* terminate previous */
*to++ = 3; /* save the ^C */
}
break; /* and back to data */
case SE:
case DM:
// printf("RCVD TS_IAC SE/DM\n");
state = TS_SB;
break;
case NOP:
case GA:
default:
// printf("RCVD TS_IAC NOP/GA/default\n");
break;
}
state = TS_DATA;
continue;
case TS_WILL:
#if 0
// printf("RCVD TS_WILL %s\n", telopts[ch]);
if (ch == TELOPT_LINEMODE)
{
// printf("SENT IAC SB LINEMODE SE\n");
add_message("%c%c%c%c%c%c%c", IAC, SB, TELOPT_LINEMODE,
LM_MODE, MODE_EDIT, IAC, SE);
}
if (ch == TELOPT_ECHO)
{
// printf("SEND DO ECHO\n");
add_message("%c%c%c", IAC, DO, TELOPT_ECHO);
}
#endif
state = TS_DATA;
continue;
case TS_WONT:
// printf("RCVD TS_WONT %s\n", telopts[ch]);
#if 0
if (ch == TELOPT_ECHO)
{
// printf("SEND DONT ECHO\n");
add_message("%c%c%c", IAC, DONT, TELOPT_ECHO);
}
#endif
state = TS_DATA;
continue;
case TS_DO:
/* we understand these (for ^C) but nothing else */
// printf("RCVD TS_DO %s\n", telopts[ch]);
if (ch == TELOPT_TM)
{
needs_tm = 1;
}
else if (ch == TELOPT_COMPRESS)
{
char init_buf[5] = { IAC, SB, TELOPT_COMPRESS, WILL, SE };
binary_output(ip, init_buf, 5);
start_compression(ip);
}
else if (ch == TELOPT_COMPRESS2)
{
char init_buf[5] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE };
// printf("SENT IAC SB COMPRESS2 IAC SE\n");
binary_output(ip, init_buf, 5);
start_compression(ip);
}
state = TS_DATA;
continue;
case TS_DONT:
if (ch == TELOPT_COMPRESS2)
{
char init_buf[3] = { IAC, WILL, TELOPT_COMPRESS };
binary_output(ip, init_buf, 3);
}
else if (ch == TELOPT_COMPRESS)
{
end_compression(ip);
}
state = TS_DATA;
continue;
case TS_SB:
if (ch == IAC)
{
// printf("RCVD TS_SB IAC\n");
state = TS_SE;
}
else
{
// printf("RCVD TS_SB %d ", (int) ch);
}
continue;
default:
debug_message("Bad state: 0x%x\n", (char *)state);
state = TS_DATA;
continue;
}
}
return 0;
}
#ifdef INET_NTOA_OK
char *
query_ip_number(Obj *ob)
{
if (ob == 0) ob = command_giver;
if (!ob || ob->interactive == 0) return 0;
if (ob->interactive->ipaddr) return ob->interactive->ipaddr->str;
return inet_ntoa(ob->interactive->addr.sin_addr);
}
#else /* INET_NTOA_OK */
/*
* Note: if the address string is "a.b.c.d" the address number is a * 256^3 +
* b * 256^2 + c * 256 + d
*
*/
char *
query_ip_number(Obj *ob)
{
u_long s_ad;
int a, b, c, d;
static char addr[20]; /* 16 + 1 should be enough */
if (ob == 0)
ob = command_giver;
if (!ob || ob->interactive == 0)
return 0;
s_ad = ob->interactive->addr.sin_addr.s_addr;
d = s_ad & 0xff;
c = (s_ad & 0xff00) >> 8;
b = (s_ad & 0xff0000) >> 16;
a = (s_ad & 0xff000000) >> 24;
sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
return addr;
}
#endif /* INET_NTOA_OK */
int set_ip_number(char *s, Obj *o)
{
if (!o || !o->interactive)
return 0;
if (o->interactive->ipaddr) {
free_string(o->interactive->ipaddr);
o->interactive->ipaddr = 0;
}
if (s)
o->interactive->ipaddr = string_copy(s);
return 1;
}
struct interactive *
find_extern_pid(int ProcId)
{
int Count;
struct interactive *Player;
for (Count = 0; Count < MAX_PLAYERS; Count++) {
Player = all_players[Count];
if (Player && Player->extern_pid == ProcId)
return Player;
}
return 0;
}
void add_message(const char *fmt, ...)
{
static char mess_buff[65536];
Val * o;
va_list args;
if (command_giver == NULL || command_giver->destructed) return;
#if 0
printf("In #add_message(c_g=%s)#.\n", command_giver->name);
#endif
va_start(args, fmt);
vsprintf(mess_buff, fmt, args);
va_end(args);
o = make_string(mess_buff);
apply_clean(C("catch_output"), command_giver, o);
}
#ifdef HAVE_ZLIB_H
//
// Compression code: Dwayne Fontenot (Jacques@TMI)
// Changes (bugs, fixes, etc): Geoff Wong.
//
void* zlib_alloc(void* opaque, unsigned int items, unsigned int size)
{
return calloc(items, size);
}
void zlib_free(void* opaque, void* address)
{
free(address);
}
void end_compression(struct interactive * ip)
{
unsigned char dummy[1];
printf("end compression\n");
if (!ip->my_z_stream) return;
ip->my_z_stream->avail_in = 0;
ip->my_z_stream->next_in = dummy;
if (deflate(ip->my_z_stream, Z_FINISH) != Z_STREAM_END)
{
// FIX: flush properly? Or clear any buffers?
}
deflateEnd(ip->my_z_stream);
free(ip->my_z_stream);
ip->my_z_stream = NULL;
}
void start_compression(struct interactive * ip)
{
z_stream * compress;
printf("start compression\n");
if (ip->my_z_stream) return;
compress = (z_stream *) malloc(sizeof(z_stream));
compress->next_in = NULL;
compress->avail_in = 0;
compress->next_out = ip->output;
compress->avail_out = OUTPUT_BUFFER_SIZE;
compress->zalloc = zlib_alloc;
compress->zfree = zlib_free;
compress->opaque = NULL;
if (deflateInit(compress, 9) != Z_OK)
{
free(compress);
ip->my_z_stream = NULL;
fprintf(stderr, "Compression failed.\n");
return;
}
// Ok, compressing.
ip->my_z_stream = compress;
}
int flush_compressed_output(struct interactive * ip)
{
int iStart, nBlock, nWrite, len;
z_stream * compress;
//printf("flush compressed\n");
if (!ip->my_z_stream) return 1;
compress = ip->my_z_stream;
/* Try to write out some data.. */
len = (int) compress->next_out - (int) ip->output;
/* we have some data to write */
if (len > 0)
{
nWrite = 0;
for (iStart = 0; iStart < len; iStart += nWrite)
{
if (len - iStart < 4096)
{
nBlock = len - iStart;
}
else
{
nBlock = 4096;
}
nWrite = write(ip->socket, &ip->output[iStart], nBlock);
if (nWrite < 0)
{
fprintf(stderr, "Error sending compressed data (%d)\n",
errno);
if (errno == EAGAIN || errno == ENOSR) break;
/* write error */
return 0;
}
if (nWrite <= 0) break;
}
if (iStart)
{
/* We wrote "iStart" bytes */
if (iStart < len)
{
memmove(ip->output, ip->output+iStart, len - iStart);
}
compress->next_out = ip->output + len - iStart;
}
}
return iStart;
}
int write_compressed(struct interactive * ip, char * data, int length)
{
z_stream * compress;
//printf("write compressed\n");
compress = ip->my_z_stream;
compress->next_in = data;
compress->avail_in = length;
while (compress->avail_in)
{
compress->avail_out = OUTPUT_BUFFER_SIZE -
((int) compress->next_out - (int) ip->output);
if (compress->avail_out)
deflate(compress, Z_SYNC_FLUSH);
// Was there an error?
if (flush_compressed_output(ip) == 0) return 0;
}
return length;
}
#endif