mud/fluffos-2.23-ds03/comm.c
2020-09-06 05:43:07 -07:00

3348 lines
108 KiB
C

/*
* comm.c -- communications functions and more.
* Dwayne Fontenot (Jacques@TMI)
*/
#include "std.h"
#include "comm.h"
#include "main.h"
#include "socket_efuns.h"
#include "backend.h"
#include "socket_ctrl.h"
#include "debug.h"
#include "ed.h"
#include "file.h"
#include "master.h"
#include "add_action.h"
#include "eval.h"
#include "console.h"
#ifndef ENOSR
#define ENOSR 63
#endif
#ifndef ANSI_SUBSTITUTE
#define ANSI_SUBSTITUTE 0x20
#endif
#ifndef ADDRFAIL_NOTIFY
#define ADDRFAIL_NOTIFY 0
#endif
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#define TELOPT_MSSP 70
#define TELOPT_COMPRESS 85
#define TELOPT_COMPRESS2 86
#define TELOPT_MXP 91 // mud extension protocol
#define TELOPT_ZMP 93 // zenith mud protocol
#define TELOPT_GMCP 201 // something mud communication protocol, how many do we need?
#define MSSP_VAR 1
#define MSSP_VAL 2
#ifndef MAX
#define MAX(x,y) (((x)>(y))?(x):(y))
#endif
#ifndef ENV_FILLER
#define ENV_FILLER 0x1e
#endif
#define TELOPT_NEW_ENVIRON 39
#define NEW_ENV_IS 0
#define NEW_ENV_SEND 1
#define NEW_ENV_INFO 2
#define NEW_ENV_VAR 0
#define NEW_ENV_VALUE 1
#define NEW_ENV_ESC 2
#define NEW_ENV_USERVAR 3
static unsigned char telnet_break_response[] = { 28, IAC, WILL, TELOPT_TM };
static unsigned char telnet_ip_response[] = { 127, IAC, WILL, TELOPT_TM };
static unsigned char telnet_abort_response[] = { IAC, DM };
static unsigned char telnet_do_tm_response[] = { IAC, WILL, TELOPT_TM };
static unsigned char telnet_do_naws[] = { IAC, DO, TELOPT_NAWS };
static unsigned char telnet_dont_naws[] = { IAC, DONT, TELOPT_NAWS };
static unsigned char telnet_do_ttype[] = { IAC, DO, TELOPT_TTYPE };
static unsigned char telnet_term_query[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
static unsigned char telnet_no_echo[] = { IAC, WONT, TELOPT_ECHO };
static unsigned char telnet_no_single[] = { IAC, WONT, TELOPT_SGA };
static unsigned char telnet_yes_echo[] = { IAC, WILL, TELOPT_ECHO };
static unsigned char telnet_yes_single[] = { IAC, WILL, TELOPT_SGA };
static unsigned char telnet_ga[] = { IAC, GA };
static unsigned char telnet_ayt_response[] = { '\n', '[', '-', 'Y', 'e', 's', '-', ']', ' ', '\n' };
static unsigned char telnet_line_mode[] = { IAC, DO, TELOPT_LINEMODE };
static unsigned char telnet_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT | MODE_TRAPSIG, IAC, SE };
static unsigned char telnet_char_mode[] = { IAC, DONT, TELOPT_LINEMODE };
static unsigned char slc_default_flags[] = { SLC_NOSUPPORT, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_NOSUPPORT,
SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_NOSUPPORT, SLC_NOSUPPORT,
SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT };
static unsigned char slc_default_chars[] = { 0x00, BREAK, IP, AO, AYT, 0x00, 0x00, 0x00,
SUSP, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00 };
#ifdef HAVE_ZLIB
static unsigned char telnet_compress_send_request_v2[] = { IAC, WILL,
TELOPT_COMPRESS2 };
static unsigned char telnet_compress_send_request_v1[] = { IAC, WILL,
TELOPT_COMPRESS };
static unsigned char telnet_compress_v1_response[] = { IAC, SB,
TELOPT_COMPRESS, WILL,
SE };
static unsigned char telnet_compress_v2_response[] = { IAC, SB,
TELOPT_COMPRESS2, IAC,
SE };
#endif
static unsigned char telnet_do_mxp[] = { IAC, DO, TELOPT_MXP };
static unsigned char telnet_will_mxp[] = { IAC, SB, TELOPT_MXP, IAC, SE };
static unsigned char telnet_will_mssp[] = { IAC, WILL, TELOPT_MSSP };
static unsigned char telnet_start_mssp[] = { IAC, SB, TELOPT_MSSP };
static unsigned char telnet_mssp_value[] = {MSSP_VAR, '%', 's', MSSP_VAL, '%', 's', 0};
static unsigned char telnet_end_sub[] = {IAC, SE};
static unsigned char telnet_will_zmp[] = { IAC, WILL, TELOPT_ZMP};
static unsigned char telnet_start_zmp[] = { IAC, SB, TELOPT_ZMP};
static unsigned char telnet_do_newenv[] = { IAC, DO, TELOPT_NEW_ENVIRON };
static unsigned char telnet_send_uservar[] = { IAC, SB, TELOPT_NEW_ENVIRON, NEW_ENV_SEND, IAC, SE };
static unsigned char telnet_do_gmcp[] = {IAC, DO, TELOPT_GMCP};
static unsigned char telnet_start_gmcp[] = {IAC, SB, TELOPT_GMCP};
/*
* local function prototypes.
*/
#ifdef SIGNAL_FUNC_TAKES_INT
static void sigpipe_handler (int);
#else
static void sigpipe_handler (void);
#endif
static void hname_handler (void);
static void get_user_data (interactive_t *);
static char *get_user_command (void);
static char *first_cmd_in_buf (interactive_t *);
static int cmd_in_buf (interactive_t *);
static int call_function_interactive (interactive_t *, char *);
static void print_prompt (interactive_t *);
static void query_addr_name (object_t *);
static void got_addr_number (char *, char *);
#ifdef IPV6
static void add_ip_entry (struct in6_addr, char *);
#else
static void add_ip_entry (long, char *);
#endif
static void new_user_handler (int);
static void end_compression (interactive_t *);
static void start_compression (interactive_t *);
static int send_compressed (interactive_t *ip, unsigned char* data, int length);
#ifdef NO_SNOOP
# define handle_snoop(str, len, who)
#else
# define handle_snoop(str, len, who) if ((who)->snooped_by) receive_snoop(str, len, who->snooped_by)
static void receive_snoop (const char *, int, object_t * ob);
#endif
/*
* public local variables.
*/
fd_set readmask, writemask;
int num_user;
#ifdef F_SET_HIDE
int num_hidden_users = 0; /* for the O_HIDDEN flag. This counter must
* be kept up to date at all times! If you
* modify the O_HIDDEN flag in an object,
* make sure that you update this counter if
* the object is interactive. */
#endif
int add_message_calls = 0;
#ifdef F_NETWORK_STATS
int inet_out_packets = 0;
int inet_out_volume = 0;
int inet_in_packets = 0;
int inet_in_volume = 0;
#ifdef PACKAGE_SOCKETS
int inet_socket_in_packets = 0;
int inet_socket_in_volume = 0;
int inet_socket_out_packets = 0;
int inet_socket_out_volume = 0;
#endif
#endif
int inet_packets = 0;
int inet_volume = 0;
interactive_t **all_users = 0;
int max_users = 0;
#ifdef HAS_CONSOLE
int has_console = -1;
#endif
/*
* private local variables.
*/
static int addr_server_fd = -1;
static
void set_linemode (interactive_t * ip)
{
if (ip->iflags & USING_LINEMODE) {
add_binary_message(ip->ob, telnet_line_mode, sizeof(telnet_line_mode));
add_binary_message(ip->ob, telnet_lm_mode, sizeof(telnet_lm_mode));
} else {
add_binary_message(ip->ob, telnet_no_single, sizeof(telnet_no_single));
}
}
static
void set_charmode (interactive_t * ip)
{
if (ip->iflags & USING_LINEMODE) {
add_binary_message(ip->ob, telnet_char_mode, sizeof(telnet_char_mode));
} else {
add_binary_message(ip->ob, telnet_yes_single, sizeof(telnet_yes_single));
}
}
#ifndef NO_SNOOP
static void
receive_snoop (const char * buf, int len, object_t * snooper)
{
/* command giver no longer set to snooper */
#ifdef RECEIVE_SNOOP
char *str;
str = new_string(len, "receive_snoop");
memcpy(str, buf, len);
str[len] = 0;
push_malloced_string(str);
apply(APPLY_RECEIVE_SNOOP, snooper, 1, ORIGIN_DRIVER);
#else
/* snoop output is now % in all cases */
add_message(snooper, "%", 1);
add_message(snooper, buf, len);
#endif
}
#endif
/*
* Initialize new user connection socket.
*/
void init_user_conn()
{
#ifdef IPV6
struct sockaddr_in6 sin;
#else
struct sockaddr_in sin;
#endif
memset(&sin, 0, sizeof(sin));
socklen_t sin_len;
int optval;
int i;
int have_fd6;
int fd6_which = -1;
/* Check for fd #6 open as a valid socket */
optval = 1;
have_fd6 = (setsockopt(6, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == 0);
for (i=0; i < 5; i++) {
#ifdef F_NETWORK_STATS
external_port[i].in_packets = 0;
external_port[i].in_volume = 0;
external_port[i].out_packets = 0;
external_port[i].out_volume = 0;
#endif
if (!external_port[i].port) {
#if defined(FD6_KIND) && defined(FD6_PORT)
if (!have_fd6) continue;
fd6_which = i;
have_fd6 = 0;
if (FD6_KIND == PORT_UNDEFINED || FD6_PORT < 1) {
debug_message("Socket passed to fd 6 ignored (support is disabled).\n");
continue;
}
debug_message("Accepting connections on fd 6 (port %d).\n", FD6_PORT);
external_port[i].kind = FD6_KIND;
external_port[i].port = FD6_PORT;
external_port[i].fd = 6;
#else
continue;
#endif
} else {
/*
* create socket of proper type.
*/
int sockflags = SOCK_STREAM;
#ifdef IPV6
if ((external_port[i].fd = socket(PF_INET6, sockflags, 0)) == -1) {
#else
if ((external_port[i].fd = socket(PF_INET, sockflags, 0)) == -1) {
#endif
debug_perror("init_user_conn: socket", 0);
exit(1);
}
/*
* enable local address reuse.
*/
optval = 1;
if (setsockopt(external_port[i].fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &optval, sizeof(optval)) == -1) {
socket_perror("init_user_conn: setsockopt", 0);
exit(2);
}
#ifdef FD_CLOEXEC
fcntl(external_port[i].fd, F_SETFD, FD_CLOEXEC);
#endif
/*
* fill in socket address information.
*/
#ifdef IPV6
sin.sin6_family = AF_INET6;
if(MUD_IP[0])
inet_pton(AF_INET6, MUD_IP, &(sin.sin6_addr));
else
sin.sin6_addr = in6addr_any;
sin.sin6_port = htons((u_short) external_port[i].port);
#else
sin.sin_family = AF_INET;
if (MUD_IP[0])
sin.sin_addr.s_addr = inet_addr(MUD_IP);
else
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons((u_short) external_port[i].port);
#endif
/*
* bind name to socket.
*/
if (bind(external_port[i].fd, (struct sockaddr *) & sin,
sizeof(sin)) == -1) {
socket_perror("init_user_conn: bind", 0);
exit(3);
}
}
/*
* get socket name.
*/
sin_len = sizeof(sin);
if (getsockname(external_port[i].fd, (struct sockaddr *) & sin,
&sin_len) == -1) {
socket_perror("init_user_conn: getsockname", 0);
if (i != fd6_which) {
exit(4);
}
}
/*
* set socket non-blocking,
*/
if (set_socket_nonblocking(external_port[i].fd, 1) == -1) {
socket_perror("init_user_conn: set_socket_nonblocking 1", 0);
if (i != fd6_which) {
exit(8);
}
}
/*
* listen on socket for connections.
*/
if (listen(external_port[i].fd, 128) == -1) {
socket_perror("init_user_conn: listen", 0);
if (i != fd6_which) {
exit(10);
}
}
}
if (have_fd6) {
debug_message("No more ports available; fd #6 ignored.\n");
}
/*
* register signal handler for SIGPIPE.
*/
#if defined(SIGPIPE) && defined(SIGNAL_ERROR)
#ifdef SIG_IGN
if (signal(SIGPIPE, SIG_IGN) == SIGNAL_ERROR) {
debug_perror("init_user_conn: signal SIGPIPE",0);
exit(5);
}
#else
if (signal(SIGPIPE, sigpipe_handler) == SIGNAL_ERROR) {
debug_perror("init_user_conn: signal SIGPIPE",0);
exit(5);
}
#endif
#endif
}
/*
* Shut down new user accept file descriptor.
*/
void ipc_remove()
{
int i;
for (i = 0; i < 5; i++) {
if (!external_port[i].port) continue;
if (OS_socket_close(external_port[i].fd) == -1) {
socket_perror("ipc_remove: close", 0);
}
}
debug_message("closed external ports\n");
}
void init_addr_server (char * hostname, int addr_server_port)
{
#ifdef WIN32
WORD wVersionRequested = MAKEWORD(1,1);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
#endif
#ifdef IPV6
struct sockaddr_in6 server;
#else
struct sockaddr_in server;
#endif
#ifndef IPV6
struct hostent *hp;
int addr;
#endif
int server_fd;
int optval;
if (addr_server_fd >= 0)
return;
if (!hostname) return;
#ifdef IPV6
/*
* get network host data for hostname.
*/
struct addrinfo hints, *res;
hints.ai_family = AF_INET6;
hints.ai_socktype = 0;
hints.ai_protocol = 0;
#ifndef AI_V4MAPPED
hints.ai_flags = AI_CANONNAME;
#else
hints.ai_flags = AI_CANONNAME| AI_V4MAPPED;
#endif
if(getaddrinfo(hostname, "1234", &hints, &res)){
//failed
socket_perror("init_addr_server: getaddrinfo", 0);
return;
}
memcpy(&server, res->ai_addr, sizeof(server));
freeaddrinfo(res);
server.sin6_port = htons((u_short) addr_server_port);
//inet_pton(AF_INET6, hostname, &(server.sin6_addr));
//memcpy((char *) &server.sin6_addr, (char *) hp->h_addr, hp->h_length);
/*
* create socket of proper type.
*/
server_fd = socket(AF_INET6, SOCK_STREAM, 0);
#else
/*
* get network host data for hostname.
*/
if (hostname[0] >= '0' && hostname[0] <= '9' &&
(addr = inet_addr(hostname)) != -1) {
hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
} else {
hp = gethostbyname(hostname);
}
if (hp == NULL) {
socket_perror("init_addr_server: gethostbyname", 0);
return;
}
/*
* set up address information for server.
*/
server.sin_family = AF_INET;
server.sin_port = htons((u_short) addr_server_port);
server.sin_addr.s_addr = inet_addr(hostname);
memcpy((char *) &server.sin_addr, (char *) hp->h_addr, hp->h_length);
/*
* create socket of proper type.
*/
server_fd = socket(AF_INET, SOCK_STREAM, 0);
#endif
if (server_fd == INVALID_SOCKET) { /* problem opening socket */
socket_perror("init_addr_server: socket", 0);
return;
}
/*
* enable local address reuse.
*/
optval = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optval,
sizeof(optval)) == -1) {
socket_perror("init_addr_server: setsockopt", 0);
return;
}
/*
* connect socket to server address.
*/
if (connect(server_fd, (struct sockaddr *) & server, sizeof(server)) == -1) {
if(ADDRFAIL_NOTIFY){
if (socket_errno == ECONNREFUSED && ADDRFAIL_NOTIFY)
debug_message("Connection to address server (%s %d) refused.\n",
hostname, addr_server_port);
else
socket_perror("init_addr_server: connect", 0);
}
OS_socket_close(server_fd);
return;
}
addr_server_fd = server_fd;
debug_message("Connected to address server on %s port %d\n", hostname,
addr_server_port);
/*
* set socket non-blocking.
*/
if (set_socket_nonblocking(server_fd, 1) == -1) {
socket_perror("init_addr_server: set_socket_nonblocking 1", 0);
return;
}
#ifdef WIN32
WSACleanup();
#endif
}
/*
* If there is a shadow for this object, then the message should be
* sent to it. But only if catch_tell() is defined. Beware that one of the
* shadows may be the originator of the message, which means that we must
* not send the message to that shadow, or any shadows in the linked list
* before that shadow.
*
* Also note that we don't need to do this in the case of
* INTERACTIVE_CATCH_TELL, since catch_tell() was already called
* _instead of_ add_message(), and shadows got their chance then.
*/
#if !defined(INTERACTIVE_CATCH_TELL) && !defined(NO_SHADOWS)
#define SHADOW_CATCH_MESSAGE
#endif
#ifdef SHADOW_CATCH_MESSAGE
static int shadow_catch_message (object_t * ob, const char * str)
{
if (!ob->shadowed)
return 0;
while (ob->shadowed != 0 && ob->shadowed != current_object)
ob = ob->shadowed;
while (ob->shadowing) {
copy_and_push_string(str);
if (apply(APPLY_CATCH_TELL, ob, 1, ORIGIN_DRIVER))
/* this will work, since we know the */
/* function is defined */
return 1;
ob = ob->shadowing;
}
return 0;
}
#endif
/*
* Send a message to an interactive object. If that object is shadowed,
* special handling is done.
*/
void add_message (object_t * who, const char * data, int len)
{
interactive_t *ip;
const char *cp;
const char *end;
char *trans;
int translen;
/*
* if who->interactive is not valid, write message on stderr.
* (maybe)
*/
if (!who || (who->flags & O_DESTRUCTED) || !who->interactive ||
(who->interactive->iflags & (NET_DEAD | CLOSING))) {
#ifdef NONINTERACTIVE_STDERR_WRITE
putc(']', stderr);
fwrite(data, len, 1, stderr);
#endif
return;
}
ip = who->interactive;
trans = translate(ip->trans->outgoing, data, len, &translen);
#ifdef SHADOW_CATCH_MESSAGE
/*
* shadow handling.
*/
if (shadow_catch_message(who, data)) {
#ifdef SNOOP_SHADOWED
handle_snoop(data, len, ip);
#endif
return;
}
#endif /* NO_SHADOWS */
/*
* write message into ip->message_buf.
*/
end = trans + translen;
for (cp = trans; cp < end; cp++) {
if (ip->message_length == MESSAGE_BUF_SIZE) {
if (!flush_message(ip)) {
debug(connections, ("Broken connection during add_message."));
return;
}
if (ip->message_length == MESSAGE_BUF_SIZE)
break;
}
if ((*cp == '\n' || *cp == -1)
#ifndef NO_BUFFER_TYPE
&& ip->connection_type != PORT_BINARY
#endif
) {
if (ip->message_length == (MESSAGE_BUF_SIZE - 1)) {
if (!flush_message(ip)) {
debug(connections, ("Broken connection during add_message."));
return;
}
if (ip->message_length == (MESSAGE_BUF_SIZE - 1))
break;
}
ip->message_buf[ip->message_producer] = (*cp == '\n')?'\r':-1;
ip->message_producer = (ip->message_producer + 1)
% MESSAGE_BUF_SIZE;
ip->message_length++;
}
ip->message_buf[ip->message_producer] = *cp;
ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE;
ip->message_length++;
}
handle_snoop(data, len, ip);
#ifdef FLUSH_OUTPUT_IMMEDIATELY
flush_message(ip);
#endif
add_message_calls++;
} /* add_message() */
/* WARNING: this can only handle results < LARGEST_PRINTABLE_STRING in size */
void add_vmessage (object_t *who, const char *format, ...)
{
int len;
interactive_t *ip;
char *cp, new_string_data[LARGEST_PRINTABLE_STRING + 1];
va_list args;
V_START(args, format);
V_VAR(object_t *, who, args);
V_VAR(char *, format, args);
/*
* if who->interactive is not valid, write message on stderr.
* (maybe)
*/
if (!who || (who->flags & O_DESTRUCTED) || !who->interactive ||
(who->interactive->iflags & (NET_DEAD | CLOSING))) {
#ifdef NONINTERACTIVE_STDERR_WRITE
putc(']', stderr);
vfprintf(stderr, format, args);
#endif
va_end(args);
return;
}
ip = who->interactive;
new_string_data[0] = '\0';
vsnprintf(new_string_data, LARGEST_PRINTABLE_STRING, format, args);
va_end(args);
len = strlen(new_string_data);
#ifdef SHADOW_CATCH_MESSAGE
/*
* shadow handling.
*/
if (shadow_catch_message(who, new_string_data)) {
#ifdef SNOOP_SHADOWED
handle_snoop(new_string_data, len, ip);
#endif
return;
}
#endif /* NO_SHADOWS */
/*
* write message into ip->message_buf.
*/
for (cp = new_string_data; *cp != '\0'; cp++) {
if (ip->message_length == MESSAGE_BUF_SIZE) {
if (!flush_message(ip)) {
debug(connections, ("Broken connection during add_message."));
return;
}
if (ip->message_length == MESSAGE_BUF_SIZE)
break;
}
if (*cp == '\n') {
if (ip->message_length == (MESSAGE_BUF_SIZE - 1)) {
if (!flush_message(ip)) {
debug(connections, ("Broken connection during add_message.\n"));
return;
}
if (ip->message_length == (MESSAGE_BUF_SIZE - 1))
break;
}
ip->message_buf[ip->message_producer] = '\r';
ip->message_producer = (ip->message_producer + 1)
% MESSAGE_BUF_SIZE;
ip->message_length++;
}
ip->message_buf[ip->message_producer] = *cp;
ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE;
ip->message_length++;
}
if (ip->message_length != 0) {
if (!flush_message(ip)) {
debug(connections, ("Broken connection during add_message.\n"));
return;
}
}
handle_snoop(new_string_data, len, ip);
#ifdef FLUSH_OUTPUT_IMMEDIATELY
flush_message(ip);
#endif
add_message_calls++;
} /* add_message() */
void add_binary_message (object_t * who, const unsigned char * data, int len)
{
interactive_t *ip;
const unsigned char *cp, *end;
/*
* if who->interactive is not valid, bail
*/
if (!who || (who->flags & O_DESTRUCTED) || !who->interactive ||
(who->interactive->iflags & (NET_DEAD | CLOSING))) {
return;
}
ip = who->interactive;
/*
* write message into ip->message_buf.
*/
end = data + len;
for (cp = data; cp < end; cp++) {
if (ip->message_length == MESSAGE_BUF_SIZE) {
if (!flush_message(ip)) {
debug(connections, ("Broken connection during add_message."));
return;
}
if (ip->message_length == MESSAGE_BUF_SIZE)
break;
}
ip->message_buf[ip->message_producer] = *cp;
ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE;
ip->message_length++;
}
flush_message(ip);
add_message_calls++;
}
/*
* Flush outgoing message buffer of current interactive object.
*/
int flush_message (interactive_t * ip)
{
int length, num_bytes;
/*
* if ip is not valid, do nothing.
*/
if (!ip || !ip->ob || !IP_VALID(ip, ip->ob) ||
(ip->ob->flags & O_DESTRUCTED) || (ip->iflags & (NET_DEAD | CLOSING))){
//debug(connections, ("flush_message: invalid target!\n"));
return 0;
}
/*
* write ip->message_buf[] to socket.
*/
while (ip->message_length != 0) {
if (ip->message_consumer < ip->message_producer) {
length = ip->message_producer - ip->message_consumer;
} else {
length = MESSAGE_BUF_SIZE - ip->message_consumer;
}
/* Need to use send to get out of band data
num_bytes = write(ip->fd,ip->message_buf + ip->message_consumer,length);
*/
#ifdef HAVE_ZLIB
if (ip->compressed_stream) {
num_bytes = send_compressed(ip, (unsigned char *)ip->message_buf +
ip->message_consumer, length);
} else {
#endif
num_bytes = send(ip->fd, ip->message_buf + ip->message_consumer,
length, ip->out_of_band | MSG_NOSIGNAL);
#ifdef HAVE_ZLIB
}
#endif
if (!num_bytes) {
ip->iflags |= NET_DEAD;
return 0;
}
if (num_bytes == -1) {
#ifdef EWOULDBLOCK
if (socket_errno == EWOULDBLOCK) {
//debug(connections, ("flush_message: write: Operation would block\n"));
return 1;
#else
if (0) {
;
#endif
} else if (socket_errno == EINTR) {
//debug(connections, ("flush_message: write: Interrupted system call"));
return 1;
} else {
//socket_perror("flush_message: write", 0);
ip->iflags |= NET_DEAD;
return 0;
}
}
ip->message_consumer = (ip->message_consumer + num_bytes) %
MESSAGE_BUF_SIZE;
ip->message_length -= num_bytes;
ip->out_of_band = 0;
inet_packets++;
inet_volume += num_bytes;
#ifdef F_NETWORK_STATS
inet_out_packets++;
inet_out_volume += num_bytes;
external_port[ip->external_port].out_packets++;
external_port[ip->external_port].out_volume += num_bytes;
#endif
}
return 1;
} /* flush_message() */
static int send_mssp_val(mapping_t *map, mapping_node_t *el, void *obp){
object_t *ob = (object_t *)obp;
if(el->values[0].type == T_STRING && el->values[1].type == T_STRING){
char buf[1024];
int len = sprintf(buf, (char *)telnet_mssp_value, el->values[0].u.string, el->values[1].u.string);
add_binary_message(ob, (unsigned char *)buf, len);
} else if (el->values[0].type == T_STRING && el->values[1].type == T_ARRAY && el->values[1].u.arr->size > 0 && el->values[1].u.arr->item[0].type == T_STRING){
char buf[10240];
int len = sprintf(buf, (char *)telnet_mssp_value, el->values[0].u.string, el->values[1].u.arr->item[0].u.string);
add_binary_message(ob, (unsigned char *)buf, len);
array_t *ar = el->values[1].u.arr;
int i;
unsigned char val = MSSP_VAL;
for(i=1; i < ar->size; i++){
if(ar->item[i].type == T_STRING){
add_binary_message(ob, &val, 1);
add_binary_message(ob, (const unsigned char *)ar->item[i].u.string, strlen(ar->item[i].u.string));
}
}
}
return 0;
}
static void copy_chars (interactive_t * ip, char * from, int num_bytes)
{
int i, start, x;
unsigned char dont_response[3] = { IAC, DONT, 0 };
unsigned char wont_response[3] = { IAC, WONT, 0 };
start = ip->text_end;
for (i = 0; i < num_bytes; i++) {
switch (ip->state) {
case TS_DATA:
switch ((unsigned char)from[i]) {
case IAC:
ip->state = TS_IAC;
break;
#if defined(NO_ANSI) && defined(STRIP_BEFORE_PROCESS_INPUT)
case 0x1b:
ip->text[ip->text_end++] = ANSI_SUBSTITUTE;
break;
#endif
case 0x08:
case 0x7f:
if (ip->iflags & SINGLE_CHAR)
ip->text[ip->text_end++] = from[i];
else {
if (ip->text_end > 0)
ip->text_end--;
}
break;
default:
ip->text[ip->text_end++] = from[i];
break;
}
break;
case TS_IAC:
switch ((unsigned char)from[i]) {
case IAC:
ip->state = TS_DATA;
ip->text[ip->text_end++] = from[i];
break;
case WILL:
ip->state = TS_WILL;
break;
case WONT:
ip->state = TS_WONT;
break;
case DO:
ip->state = TS_DO;
break;
case DONT:
ip->state = TS_DONT;
break;
case SB:
ip->state = TS_SB;
ip->sb_pos = 0;
break;
case BREAK:
add_binary_message(ip->ob, telnet_break_response, sizeof(telnet_break_response));
break;
case IP: /* interrupt process */
add_binary_message(ip->ob, telnet_ip_response, sizeof(telnet_ip_response));
break;
case AYT: /* are you there? you bet */
add_binary_message(ip->ob, telnet_ayt_response, sizeof(telnet_ayt_response));
break;
case AO: /* abort output */
flush_message(ip);
ip->out_of_band = MSG_OOB;
add_binary_message(ip->ob, telnet_abort_response, sizeof(telnet_abort_response));
break;
default:
ip->state = TS_DATA;
break;
}
break;
case TS_WILL:
ip->iflags |= USING_TELNET;
switch ((unsigned char)from[i]) {
case TELOPT_TTYPE:
add_binary_message(ip->ob, telnet_term_query, sizeof(telnet_term_query));
break;
case TELOPT_LINEMODE:
/* Do linemode and set the mode: EDIT + TRAPSIG */
ip->iflags |= USING_LINEMODE;
set_linemode(ip);
break;
case TELOPT_ECHO:
case TELOPT_NAWS:
/* do nothing, but don't send a dont response */
break;
case TELOPT_MXP :
/* Mxp is enabled, tell the mudlib about it. */
apply(APPLY_MXP_ENABLE, ip->ob, 0, ORIGIN_DRIVER);
ip->iflags |= USING_MXP;
break;
case TELOPT_GMCP:
apply(APPLY_GMCP_ENABLE, ip->ob, 0, ORIGIN_DRIVER);
ip->iflags |= USING_GMCP;
break;
case TELOPT_NEW_ENVIRON :
add_binary_message(ip->ob, telnet_send_uservar, sizeof(telnet_send_uservar));
break;
default:
dont_response[2] = from[i];
add_binary_message(ip->ob, dont_response, sizeof(dont_response));
break;
}
ip->state = TS_DATA;
break;
case TS_WONT:
ip->iflags |= USING_TELNET;
switch ((unsigned char)from[i]) {
case TELOPT_LINEMODE:
/* If we're in single char mode, we just requested for
* linemode to be disabled, so don't remove our flag.
*/
if (!(ip->iflags & SINGLE_CHAR))
ip->iflags &= ~USING_LINEMODE;
break;
}
ip->state = TS_DATA;
break;
case TS_DO:
switch ((unsigned char)from[i]) {
case TELOPT_TM:
add_binary_message(ip->ob, telnet_do_tm_response, sizeof(telnet_do_tm_response));
break;
case TELOPT_SGA:
if (ip->iflags & USING_LINEMODE) {
ip->iflags |= SUPPRESS_GA;
add_binary_message(ip->ob, telnet_yes_single, sizeof(telnet_yes_single));
} else {
if (ip->iflags & SINGLE_CHAR)
add_binary_message(ip->ob, telnet_yes_single, sizeof(telnet_yes_single));
else
add_binary_message(ip->ob, telnet_no_single, sizeof(telnet_no_single));
}
break;
case TELOPT_ECHO:
/* do nothing, but don't send a wont response */
break;
case TELOPT_MSSP:
{
add_binary_message(ip->ob, telnet_start_mssp, sizeof(telnet_start_mssp));
svalue_t *res = apply_master_ob(APPLY_GET_MUD_STATS, 0);
mapping_t *map;
if(res <= 0 || res->type != T_MAPPING) {
map = allocate_mapping(0);
free_svalue(&apply_ret_value, "telnet neg");
apply_ret_value.type = T_MAPPING;
apply_ret_value.u.map = map;
} else
map = res->u.map;
//ok, so we have a mapping, first make sure we send the required values
char *tmp = findstring("NAME");
if(tmp){
svalue_t *name = find_string_in_mapping(map, tmp);
if(!name || name->type != T_STRING)
tmp = 0;
}
if(!tmp){
char buf[1024];
int len = sprintf(buf, (char *)telnet_mssp_value, "NAME", MUD_NAME);
add_binary_message(ip->ob, (unsigned char *)buf, len);
}
tmp = findstring("PLAYERS");
if(tmp){
svalue_t *players = find_string_in_mapping(map, tmp);
if(!players || players->type != T_STRING)
tmp = 0;
}
if(!tmp){
char buf[1024];
char num[5];
sprintf(num, "%d", num_user);
int len = sprintf(buf, (char *)telnet_mssp_value, "PLAYERS", num);
add_binary_message(ip->ob, (unsigned char *)buf, len);
}
tmp = findstring("UPTIME");
if(tmp){
svalue_t *upt = find_string_in_mapping(map, tmp);
if(!upt || upt->type != T_STRING)
tmp = 0;
}
if(!tmp){
char buf[1024];
char num[20];
sprintf(num, "%ld", boot_time);
int len = sprintf(buf, (char *)telnet_mssp_value, "UPTIME", num);
add_binary_message(ip->ob, (unsigned char *)buf, len);
}
//now send the rest
mapTraverse(map, send_mssp_val, ip->ob);
add_binary_message(ip->ob, telnet_end_sub, sizeof(telnet_end_sub));
}
#ifdef HAVE_ZLIB
case TELOPT_COMPRESS :
if(!ip->compressed_stream){
add_binary_message(ip->ob, telnet_compress_v1_response,
sizeof(telnet_compress_v1_response));
start_compression(ip);
}
break;
case TELOPT_COMPRESS2 :
if(!ip->compressed_stream){
add_binary_message(ip->ob, telnet_compress_v2_response,
sizeof(telnet_compress_v2_response));
start_compression(ip);
}
break;
#endif
case TELOPT_ZMP :
ip->iflags |= USING_ZMP;
break;
default:
wont_response[2] = from[i];
add_binary_message(ip->ob, wont_response, sizeof(wont_response));
break;
}
ip->state = TS_DATA;
break;
case TS_DONT:
switch ((unsigned char)from[i]) {
case TELOPT_SGA:
if (ip->iflags & USING_LINEMODE) {
ip->iflags &= ~SUPPRESS_GA;
add_binary_message(ip->ob, telnet_no_single, sizeof(telnet_no_single));
}
break;
#ifdef HAVE_ZLIB
case TELOPT_COMPRESS2:
// If we are told not to use v2, then try v1.
add_binary_message(ip->ob, telnet_compress_send_request_v1,
sizeof(telnet_compress_send_request_v1));
break;
#endif
}
ip->state = TS_DATA;
break;
case TS_SB:
if ((unsigned char)from[i] == IAC) {
ip->state = TS_SB_IAC;
break;
}
if (ip->sb_pos < ip->sb_size - 1)
ip->sb_buf[ip->sb_pos++] = from[i];
else if(ip->sb_size < MAX_STRING_LENGTH){
ip->sb_size*=2;
if(ip->sb_size > MAX_STRING_LENGTH)
ip->sb_size = MAX_STRING_LENGTH;
ip->sb_buf = (char *)REALLOC(ip->sb_buf, ip->sb_size);
if (ip->sb_pos < ip->sb_size - 1)
ip->sb_buf[ip->sb_pos++] = from[i];
}
break;
case TS_SB_IAC:
switch ((unsigned char)from[i]) {
case IAC:
if (ip->sb_pos < SB_SIZE - 1) {
ip->sb_buf[ip->sb_pos++] = from[i];
ip->state = TS_SB;
}
break;
case SE:
ip->state = TS_DATA;
ip->sb_buf[ip->sb_pos] = 0;
switch ((unsigned char)ip->sb_buf[0]) {
case TELOPT_NEW_ENVIRON :
{
int j, k;
char env_buf[BUF_SIZE];
j = 0;
k = 1;
while(ip->sb_buf[k] > -1 && k < (ip->sb_pos - 1)){
k++;
if(!(ip->sb_buf[k])) env_buf[j] = ENV_FILLER;
if(ip->sb_buf[k] == 1) env_buf[j] = 1;
if((ip->sb_buf[k] > 31)){
env_buf[j] = ip->sb_buf[k];
}
if(env_buf[j]) j++;
}
env_buf[j] = 0;
copy_and_push_string(env_buf);
apply(APPLY_RECEIVE_ENVIRON, ip->ob, 1, ORIGIN_DRIVER);
break;
}
case TELOPT_LINEMODE:
switch ((unsigned char)ip->sb_buf[1]) {
case LM_MODE:
/* Don't do anything with an ACK */
if (!(ip->sb_buf[2] & MODE_ACK)) {
unsigned char sb_ack[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT | MODE_TRAPSIG | MODE_ACK, IAC, SE };
/* Accept only EDIT and TRAPSIG && force them too */
add_binary_message(ip->ob, sb_ack, sizeof(sb_ack));
}
break;
case LM_SLC:
{
int slc_length = 4;
unsigned char slc_response[SB_SIZE + 6] = { IAC, SB, TELOPT_LINEMODE, LM_SLC };
for (x = 2; x < ip->sb_pos; x += 3) {
/* no response for an ack */
if (ip->sb_buf[x + 1] & SLC_ACK)
continue;
/* If we get { 0, SLC_DEFAULT, 0 } or { 0, SLC_VARIABLE, 0 } return a list of values */
/* If it's SLC_DEFAULT, reset to defaults first */
if (!ip->sb_buf[x] && !ip->sb_buf[x + 2]) {
if (ip->sb_buf[x + 1] == SLC_DEFAULT || ip->sb_buf[x + 1] == SLC_VARIABLE) {
int n;
for (n = 0; n < NSLC; n++) {
slc_response[slc_length++] = n + 1;
if (ip->sb_buf[x + 1] == SLC_DEFAULT) {
ip->slc[n][0] = slc_default_flags[n];
ip->slc[n][1] = slc_default_chars[n];
slc_response[slc_length++] = SLC_DEFAULT;
} else {
slc_response[slc_length++] = ip->slc[n][0];
}
slc_response[slc_length++] = ip->slc[n][1];
}
break;
}
}
slc_response[slc_length++] = ip->sb_buf[x]--;
/* If the first octet is out of range, we don't support it */
/* If the default flag is not supported, we don't support it */
if (ip->sb_buf[x] >= NSLC || slc_default_flags[ip->sb_buf[x]] == SLC_NOSUPPORT) {
slc_response[slc_length++] = SLC_NOSUPPORT;
slc_response[slc_length++] = ip->sb_buf[x + 2];
if ((unsigned char)ip->sb_buf[x + 2] == IAC)
slc_response[slc_length++] = IAC;
continue;
}
switch ((ip->sb_buf[x + 1] & SLC_LEVELBITS)) {
case SLC_NOSUPPORT:
if (slc_default_flags[ip->sb_buf[x]] == SLC_CANTCHANGE) {
slc_response[slc_length++] = SLC_CANTCHANGE;
slc_response[slc_length++] = ip->slc[ip->sb_buf[x]][1];
break;
}
slc_response[slc_length++] = SLC_ACK | SLC_NOSUPPORT;
slc_response[slc_length++] = ip->sb_buf[x + 2];
ip->slc[ip->sb_buf[x]][0] = SLC_NOSUPPORT;
ip->slc[ip->sb_buf[x]][1] = 0;
break;
case SLC_VARIABLE:
if (slc_default_flags[ip->sb_buf[x]] == SLC_CANTCHANGE) {
slc_response[slc_length++] = SLC_CANTCHANGE;
slc_response[slc_length++] = ip->slc[ip->sb_buf[x]][1];
break;
}
slc_response[slc_length++] = SLC_ACK | SLC_VARIABLE;
slc_response[slc_length++] = ip->sb_buf[x + 2];
ip->slc[ip->sb_buf[x]][0] = ip->sb_buf[x + 1];
ip->slc[ip->sb_buf[x]][1] = ip->sb_buf[x + 2];
break;
case SLC_CANTCHANGE:
slc_response[slc_length++] = SLC_ACK | SLC_CANTCHANGE;
slc_response[slc_length++] = ip->sb_buf[x + 2];
ip->slc[ip->sb_buf[x]][0] = ip->sb_buf[x + 1];
ip->slc[ip->sb_buf[x]][1] = ip->sb_buf[x + 2];
break;
case SLC_DEFAULT:
slc_response[slc_length++] = slc_default_flags[ip->sb_buf[x]];
slc_response[slc_length++] = slc_default_flags[ip->sb_buf[x]];
ip->slc[ip->sb_buf[x]][0] = slc_default_flags[ip->sb_buf[x]];
ip->slc[ip->sb_buf[x]][1] = slc_default_chars[ip->sb_buf[x]];
break;
default:
slc_response[slc_length++] = SLC_NOSUPPORT;
slc_response[slc_length++] = ip->sb_buf[x + 2];
if ((unsigned char)slc_response[slc_length - 1] == IAC)
slc_response[slc_length++] = IAC;
break;
}
}
if (slc_length > 4) {
/* send our response */
slc_response[slc_length++] = IAC;
slc_response[slc_length++] = SE;
add_binary_message(ip->ob, slc_response, slc_length);
}
}
break;
case DO:
{
unsigned char sb_wont[] = { IAC, SB, TELOPT_LINEMODE, WONT, 0, IAC, SE };
/* send back IAC SB TELOPT_LINEMODE WONT x IAC SE */
sb_wont[4] = ip->sb_buf[2];
add_binary_message(ip->ob, sb_wont, sizeof(sb_wont));
}
break;
case WILL:
{
unsigned char sb_dont[] = { IAC, SB, TELOPT_LINEMODE, DONT, 0, IAC, SE };
/* send back IAC SB TELOPT_LINEMODE DONT x IAC SE */
sb_dont[4] = ip->sb_buf[2];
add_binary_message(ip->ob, sb_dont, sizeof(sb_dont));
}
break;
}
break;
case TELOPT_NAWS:
if (ip->sb_pos >= 5) {
push_number(((unsigned char)ip->sb_buf[1] << 8) | (unsigned char)ip->sb_buf[2]);
push_number(((unsigned char)ip->sb_buf[3] << 8) | (unsigned char)ip->sb_buf[4]);
apply(APPLY_WINDOW_SIZE, ip->ob, 2, ORIGIN_DRIVER);
}
break;
case TELOPT_TTYPE:
if (!ip->sb_buf[1]) {
copy_and_push_string(ip->sb_buf + 2);
apply(APPLY_TERMINAL_TYPE, ip->ob, 1, ORIGIN_DRIVER);
}
break;
case TELOPT_ZMP:
{
array_t *arr = allocate_array(max_array_size);
ip->sb_buf = (char *)REALLOC(ip->sb_buf, MAX(ip->sb_pos + 2, SB_SIZE));
ip->sb_size = MAX(ip->sb_pos + 2, SB_SIZE);
ip->sb_buf[ip->sb_pos] = 0;
copy_and_push_string(ip->sb_buf+1);
int off=0;
int aro = 0;
while(1){
off += strlen(ip->sb_buf+1+off)+2;
if(off >= ip->sb_pos-1)
break;
arr->item[aro].u.string = string_copy(&ip->sb_buf[off], "ZMP");
arr->item[aro].type = T_STRING;
arr->item[aro++].subtype = STRING_MALLOC;
}
arr = resize_array(arr, aro);
push_refed_array(arr);
apply(APPLY_ZMP, ip->ob, 2, ORIGIN_DRIVER);
}
break;
case TELOPT_GMCP:
ip->sb_buf[ip->sb_pos] = 0;
copy_and_push_string(&ip->sb_buf[1]);
apply(APPLY_GMCP, ip->ob, 1, ORIGIN_DRIVER);
break;
default:
for (x = 0; x < ip->sb_pos; x++)
ip->sb_buf[x] = (ip->sb_buf[x] ? ip->sb_buf[x] : 'I');
copy_and_push_string(ip->sb_buf);
apply(APPLY_TELNET_SUBOPTION, ip->ob, 1, ORIGIN_DRIVER);
break;
}
break;
default:
/*
* Apparently, old versions of MudOS would revert to TS_DATA here.
* Later versions handle the IAC, and then go back to TS_DATA mode.
* I don't think either is proper, but the related RFC documents
* aren't clear on what to do (854, 855). It is my feeling, that
* the safest thing to do here is to ignore the option.
* -- Marius, 6-Jun-2000
*/
ip->state = TS_SB;
break;
}
break;
}
}
if (ip->text_end > start) {
/* handle snooping - snooper does not see type-ahead due to
telnet being in linemode */
if (!(ip->iflags & NOECHO))
handle_snoop(ip->text + start, ip->text_end - start, ip);
}
}
/*
* Read pending data for a user into user->interactive->text.
* This also does telnet negotiation.
*/
static void get_user_data (interactive_t * ip)
{
int num_bytes, text_space;
char buf[MAX_TEXT];
/* compute how much data we can read right now */
switch (ip->connection_type)
{
case PORT_TELNET:
text_space = MAX_TEXT - ip->text_end;
/* check if we need more space */
if (text_space < MAX_TEXT / 16) {
if (ip->text_start > 0) {
memmove(ip->text, ip->text + ip->text_start, ip->text_end - ip->text_start);
text_space += ip->text_start;
ip->text_end -= ip->text_start;
ip->text_start = 0;
}
if (text_space < MAX_TEXT / 16) {
/* the user is sending too much data. flush it */
ip->iflags |= SKIP_COMMAND;
ip->text_start = ip->text_end = 0;
text_space = MAX_TEXT;
}
}
break;
case PORT_MUD:
if (ip->text_end < 4)
text_space = 4 - ip->text_end;
else
text_space = *(volatile int *)ip->text - ip->text_end + 4;
break;
default:
text_space = sizeof(buf);
break;
}
/* read the data from the socket */
//debug(connections, ("get_user_data: read on fd %d\n", ip->fd));
num_bytes = OS_socket_read(ip->fd, buf, text_space);
if (!num_bytes) {
//if (ip->iflags & CLOSING)
// debug_message("get_user_data: tried to read from closing fd.\n");
ip->iflags |= NET_DEAD;
remove_interactive(ip->ob, 0);
return;
}
if (num_bytes == -1) {
#ifdef EWOULDBLOCK
if (socket_errno == EWOULDBLOCK) {
// debug(connections, ("get_user_data: read on fd %d: Operation would block.\n", ip->fd));
return;
}
#endif
// debug_message("get_user_data: read on fd %d\n", ip->fd);
// socket_perror("get_user_data: read", 0);
ip->iflags |= NET_DEAD;
remove_interactive(ip->ob, 0);
return;
}
#ifdef F_NETWORK_STATS
inet_in_packets++;
inet_in_volume += num_bytes;
external_port[ip->external_port].in_packets++;
external_port[ip->external_port].in_volume += num_bytes;
#endif
/* process the data that we've just read */
switch (ip->connection_type)
{
case PORT_TELNET:
copy_chars(ip, buf, num_bytes);
if (cmd_in_buf(ip))
ip->iflags |= CMD_IN_BUF;
break;
case PORT_MUD:
memcpy(ip->text + ip->text_end, buf, num_bytes);
ip->text_end += num_bytes;
if (num_bytes == text_space) {
if (ip->text_end == 4) {
*(volatile int *)ip->text = ntohl(*(int *)ip->text);
if (*(volatile int *)ip->text > MAX_TEXT - 5)
remove_interactive(ip->ob, 0);
} else {
svalue_t value;
ip->text[ip->text_end] = 0;
if (restore_svalue(ip->text + 4, &value) == 0) {
STACK_INC;
*sp = value;
} else {
push_undefined();
}
ip->text_end = 0;
apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER);
}
}
break;
case PORT_ASCII:
{
char *nl, *p;
memcpy(ip->text + ip->text_end, buf, num_bytes);
ip->text_end += num_bytes;
p = ip->text + ip->text_start;
while ((nl = ( char *)memchr(p, '\n', ip->text_end - ip->text_start))) {
ip->text_start = (nl + 1) - ip->text;
*nl = 0;
if (*(nl - 1) == '\r')
*--nl = 0;
if (!(ip->ob->flags & O_DESTRUCTED)) {
char *str;
str = new_string(nl - p, "PORT_ASCII");
memcpy(str, p, nl - p + 1);
push_malloced_string(str);
apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER);
}
if (ip->text_start == ip->text_end) {
ip->text_start = ip->text_end = 0;
break;
}
p = nl + 1;
}
}
break;
#ifndef NO_BUFFER_TYPE
case PORT_BINARY:
{
buffer_t *buffer;
buffer = allocate_buffer(num_bytes);
memcpy(buffer->item, buf, num_bytes);
push_refed_buffer(buffer);
apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER);
}
break;
#endif
}
}
static int clean_buf (interactive_t * ip)
{
/* skip null input */
while (ip->text_start < ip->text_end && !*(ip->text + ip->text_start))
ip->text_start++;
/* if we've advanced beyond the end of the buffer, reset it */
if (ip->text_start >= ip->text_end) {
ip->text_start = ip->text_end = 0;
}
/* if we're skipping the current command, check to see if it has been
completed yet. if it has, flush it and clear the skip bit */
if (ip->iflags & SKIP_COMMAND) {
char *p;
for (p = ip->text + ip->text_start; p < ip->text + ip->text_end; p++) {
if (*p == '\r' || *p == '\n') {
ip->text_start += p - (ip->text + ip->text_start) + 1;
ip->iflags &= ~SKIP_COMMAND;
return clean_buf(ip);
}
}
}
return (ip->text_end > ip->text_start);
}
static int cmd_in_buf (interactive_t * ip)
{
char *p;
/* do standard input buffer cleanup */
if (!clean_buf(ip))
return 0;
/* if we're in single character mode, we've got input */
if (ip->iflags & SINGLE_CHAR)
return 1;
/* search for a newline. if found, we have a command */
for (p = ip->text + ip->text_start; p < ip->text + ip->text_end; p++) {
if (*p == '\r' || *p == '\n')
return 1;
}
/* duh, no command */
return 0;
}
static char *first_cmd_in_buf (interactive_t * ip)
{
char *p;
#ifdef GET_CHAR_IS_BUFFERED
static char tmp[2];
#endif
/* do standard input buffer cleanup */
if (!clean_buf(ip))
return 0;
p = ip->text + ip->text_start;
/* if we're in single character mode, we've got input */
if (ip->iflags & SINGLE_CHAR) {
if (*p == 8 || *p == 127)
*p = 0;
#ifndef GET_CHAR_IS_BUFFERED
ip->text_start++;
if (!clean_buf(ip))
ip->iflags &= ~CMD_IN_BUF;
return p;
#else
tmp[0] = *p;
ip->text[ip->text_start++] = 0;
if (!clean_buf(ip))
ip->iflags &= ~CMD_IN_BUF;
return tmp;
#endif
}
/* search for the newline */
while (ip->text[ip->text_start] != '\n' && ip->text[ip->text_start] != '\r')
ip->text_start++;
/* check for "\r\n" or "\n\r" */
if (ip->text_start + 1 < ip->text_end &&
((ip->text[ip->text_start] == '\r' && ip->text[ip->text_start + 1] == '\n') ||
(ip->text[ip->text_start] == '\n' && ip->text[ip->text_start + 1] == '\r'))) {
ip->text[ip->text_start++] = 0;
}
ip->text[ip->text_start++] = 0;
if (!cmd_in_buf(ip))
ip->iflags &= ~CMD_IN_BUF;
return p;
}
/*
* SIGPIPE handler -- does very little for now.
*/
#ifndef SIG_IGN
#ifdef SIGNAL_FUNC_TAKES_INT
void sigpipe_handler (int sig)
#else
void sigpipe_handler()
#endif
{
debug(connections, ("SIGPIPE received."));
//don't comment the next line out, i'm pretty sure we'd crash on the next SIGPIPE, they're not worth it
signal(SIGPIPE, sigpipe_handler);
} /* sigpipe_handler() */
#endif
/*
* SIGALRM handler.
*/
#ifdef SIGNAL_FUNC_TAKES_INT
void sigalrm_handler (int sig)
#else
void sigalrm_handler()
#endif
{
outoftime = 1;
} /* sigalrm_handler() */
int max_fd;
INLINE void make_selectmasks()
{
int i;
max_fd = addr_server_fd;
/*
* generate readmask and writemask for select() call.
*/
FD_ZERO(&readmask);
FD_ZERO(&writemask);
#ifdef HAS_CONSOLE
/* set up a console */
if(has_console > 0)
FD_SET(STDIN_FILENO, &readmask);
#endif
/*
* set new user accept fd in readmask.
*/
for (i = 0; i < 5; i++) {
if (!external_port[i].port) continue;
FD_SET(external_port[i].fd, &readmask);
if(external_port[i].fd > max_fd)
max_fd = external_port[i].fd;
}
/*
* set user fds in readmask.
*/
for (i = 0; i < max_users; i++) {
if (!all_users[i] || (all_users[i]->iflags & (CLOSING | CMD_IN_BUF)))
continue;
/*
* if this user needs more input to make a complete command, set his
* fd so we can get it.
*/
FD_SET(all_users[i]->fd, &readmask);
if(all_users[i]->fd > max_fd)
max_fd = all_users[i]->fd;
if (all_users[i]->message_length != 0)
FD_SET(all_users[i]->fd, &writemask);
}
/*
* if addr_server_fd is set, set its fd in readmask.
*/
if (addr_server_fd >= 0) {
FD_SET(addr_server_fd, &readmask);
}
#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)
/*
* set fd's for efun sockets.
*/
for (i = 0; i < max_lpc_socks; i++) {
if (lpc_socks[i].state != STATE_CLOSED) {
if (lpc_socks[i].state != STATE_FLUSHING &&
(lpc_socks[i].flags & S_WACCEPT) == 0){
FD_SET(lpc_socks[i].fd, &readmask);
if(lpc_socks[i].fd > max_fd)
max_fd = lpc_socks[i].fd;
}
if (lpc_socks[i].flags & S_BLOCKED){
FD_SET(lpc_socks[i].fd, &writemask);
if(lpc_socks[i].fd > max_fd)
max_fd = lpc_socks[i].fd;
}
}
}
#endif
} /* make_selectmasks() */
/*
* Process I/O.
*/
INLINE void process_io()
{
int i;
/*
* check for new user connection.
*/
for (i = 0; i < 5; i++) {
if (!external_port[i].port) continue;
if (FD_ISSET(external_port[i].fd, &readmask)) {
debug(connections, ("process_io: NEW_USER\n"));
new_user_handler(i);
}
}
/*
* check for data pending on user connections.
*/
for (i = 0; i < max_users; i++) {
if (!all_users[i] || (all_users[i]->iflags & (CLOSING | CMD_IN_BUF)))
continue;
if (all_users[i]->iflags & NET_DEAD) {
remove_interactive(all_users[i]->ob, 0);
continue;
}
if (FD_ISSET(all_users[i]->fd, &readmask)) {
debug(connections, ("process_io: USER %d\n", i));
get_user_data(all_users[i]);
if (!all_users[i])
continue;
}
if (FD_ISSET(all_users[i]->fd, &writemask))
flush_message(all_users[i]);
}
#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)
/*
* check for data pending on efun socket connections.
*/
for (i = 0; i < max_lpc_socks; i++) {
if (lpc_socks[i].state != STATE_CLOSED)
if (FD_ISSET(lpc_socks[i].fd, &readmask))
socket_read_select_handler(i);
if (lpc_socks[i].state != STATE_CLOSED)
if (FD_ISSET(lpc_socks[i].fd, &writemask))
socket_write_select_handler(i);
}
#endif
/*
* check for data pending from address server.
*/
if (addr_server_fd >= 0) {
if (FD_ISSET(addr_server_fd, &readmask)) {
debug(connections, ("process_io: IP_DAEMON\n"));
hname_handler();
}
}
#ifdef HAS_CONSOLE
/* Process console input */
/* Note: need the has_console on the next line because linux (at least)
recycles fds, even STDIN_FILENO
*/
if((has_console > 0) && FD_ISSET(STDIN_FILENO, &readmask)) {
char s[1024];
int sz;
if((sz = read(STDIN_FILENO, s, 1023)) > 0) {
s[sz-1] = '\0';
console_command(s);
}
else if(sz == 0) {
printf("Console exiting. The MUD remains.\n");
has_console = 0;
}
else {
printf("Console read error: %d %d. Closing console.\n", sz, errno);
has_console = 0;
restore_sigttin();
}
}
#endif
}
/*
* This is the new user connection handler. This function is called by the
* event handler when data is pending on the listening socket (new_user_fd).
* If space is available, an interactive data structure is initialized and
* the user is connected.
*/
static void new_user_handler (int which)
{
int new_socket_fd;
#ifdef IPV6
struct sockaddr_in6 addr;
#else
struct sockaddr_in addr;
#endif
socklen_t length;
int i, x;
object_t *master, *ob;
svalue_t *ret;
length = sizeof(addr);
debug(connections, ("new_user_handler: accept on fd %d\n", external_port[which].fd));
new_socket_fd = accept(external_port[which].fd,
(struct sockaddr *) & addr, &length);
if (new_socket_fd < 0) {
#ifdef EWOULDBLOCK
if (socket_errno == EWOULDBLOCK) {
debug(connections, ("new_user_handler: accept: Operation would block\n"));
} else {
#else
if (1) {
#endif
socket_perror("new_user_handler: accept", 0);
}
return;
}
/*
* according to Amylaar, 'accepted' sockets in Linux 0.99p6 don't
* properly inherit the nonblocking property from the listening socket.
* Marius, 19-Jun-2000: this happens on other platforms as well, so just
* do it for everyone
*/
if (set_socket_nonblocking(new_socket_fd, 1) == -1) {
socket_perror("new_user_handler: set_socket_nonblocking 1", 0);
OS_socket_close(new_socket_fd);
return;
}
#if defined(SO_NOSIGPIPE)
i = 1;
if (setsockopt(new_socket_fd, 1, SO_NOSIGPIPE, &i, sizeof(i)) == -1)
{
socket_perror("new_user_handler: setsockopt SO_NOSIGPIPE", 0);
/* it's ok if this fails */
}
#endif
/* find the first available slot */
for (i = 0; i < max_users; i++)
if (!all_users[i]) break;
if (i == max_users) {
if (all_users) {
all_users = RESIZE(all_users, max_users + 10, interactive_t *,
TAG_USERS, "new_user_handler");
} else {
all_users = CALLOCATE(10, interactive_t *,
TAG_USERS, "new_user_handler");
}
while (max_users < i + 10)
all_users[max_users++] = 0;
}
set_command_giver(master_ob);
master_ob->interactive =
(interactive_t *)
DXALLOC(sizeof(interactive_t), TAG_INTERACTIVE,
"new_user_handler");
#ifndef NO_ADD_ACTION
master_ob->interactive->default_err_message.s = 0;
#endif
master_ob->interactive->connection_type = external_port[which].kind;
master_ob->interactive->sb_buf = (char *)MALLOC(SB_SIZE);
master_ob->interactive->sb_size = SB_SIZE;
master_ob->flags |= O_ONCE_INTERACTIVE;
/*
* initialize new user interactive data structure.
*/
master_ob->interactive->ob = master_ob;
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
master_ob->interactive->input_to = 0;
#endif
master_ob->interactive->iflags = 0;
master_ob->interactive->text[0] = '\0';
master_ob->interactive->text_end = 0;
master_ob->interactive->text_start = 0;
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
master_ob->interactive->carryover = NULL;
master_ob->interactive->num_carry = 0;
#endif
#ifndef NO_SNOOP
master_ob->interactive->snooped_by = 0;
#endif
master_ob->interactive->last_time = current_time;
#ifdef TRACE
master_ob->interactive->trace_level = 0;
master_ob->interactive->trace_prefix = 0;
#endif
#ifdef OLD_ED
master_ob->interactive->ed_buffer = 0;
#endif
#ifdef HAVE_ZLIB
master_ob->interactive->compressed_stream = NULL;
#endif
master_ob->interactive->message_producer = 0;
master_ob->interactive->message_consumer = 0;
master_ob->interactive->message_length = 0;
master_ob->interactive->state = TS_DATA;
master_ob->interactive->out_of_band = 0;
#ifdef USE_ICONV
master_ob->interactive->trans = get_translator("UTF-8");
#else
master_ob->interactive->trans = (struct translation *) master_ob;
//never actually used, but avoids multiple ifdefs later on!
#endif
for (x = 0; x < NSLC; x++) {
master_ob->interactive->slc[x][0] = slc_default_flags[x];
master_ob->interactive->slc[x][1] = slc_default_chars[x];
}
all_users[i] = master_ob->interactive;
all_users[i]->fd = new_socket_fd;
#ifdef F_QUERY_IP_PORT
all_users[i]->local_port = external_port[which].port;
#endif
#ifdef F_NETWORK_STATS
all_users[i]->external_port = which;
#endif
set_prompt("> ");
memcpy((char *) &all_users[i]->addr, (char *) &addr, length);
#ifdef IPV6
char tmp[INET6_ADDRSTRLEN];
debug(connections, ("New connection from %s.\n", inet_ntop(AF_INET6, &addr.sin6_addr, &tmp, INET6_ADDRSTRLEN)));
#else
debug(connections, ("New connection from %s.\n", inet_ntoa(addr.sin_addr)));
#endif
num_user++;
/*
* The user object has one extra reference. It is asserted that the
* master_ob is loaded. Save a pointer to the master ob incase it
* changes during APPLY_CONNECT. We want to free the reference on
* the right copy of the object.
*/
master = master_ob;
add_ref(master_ob, "new_user");
push_number(external_port[which].port);
ret = apply_master_ob(APPLY_CONNECT, 1);
/* master_ob->interactive can be zero if the master object self
destructed in the above (don't ask) */
set_command_giver(0);
if (ret == 0 || ret == (svalue_t *)-1 || ret->type != T_OBJECT
|| !master_ob->interactive) {
if (master_ob->interactive)
remove_interactive(master_ob, 0);
else
free_object(&master, "new_user");
#ifdef IPV6
debug_message("Connection from %s aborted.\n", inet_ntop(AF_INET6, &addr.sin6_addr, tmp, INET6_ADDRSTRLEN));
#else
debug_message("Connection from %s aborted.\n", inet_ntoa(addr.sin_addr));
#endif
return;
}
/*
* There was an object returned from connect(). Use this as the user
* object.
*/
ob = ret->u.ob;
#ifdef F_SET_HIDE
if (ob->flags & O_HIDDEN)
num_hidden_users++;
#endif
ob->interactive = master_ob->interactive;
ob->interactive->ob = ob;
ob->flags |= O_ONCE_INTERACTIVE;
/*
* assume the existance of write_prompt and process_input in user.c
* until proven wrong (after trying to call them).
*/
ob->interactive->iflags |= (HAS_WRITE_PROMPT | HAS_PROCESS_INPUT);
free_object(&master, "new_user");
master_ob->flags &= ~O_ONCE_INTERACTIVE;
master_ob->interactive = 0;
add_ref(ob, "new_user");
set_command_giver(ob);
if (addr_server_fd >= 0) {
query_addr_name(ob);
}
if (external_port[which].kind == PORT_TELNET) {
/* Ask permission to ask them for their terminal type */
add_binary_message(ob, telnet_do_ttype, sizeof(telnet_do_ttype));
/* Ask them for their window size */
add_binary_message(ob, telnet_do_naws, sizeof(telnet_do_naws));
#ifdef HAVE_ZLIB
add_binary_message(ob, telnet_compress_send_request_v2,
sizeof(telnet_compress_send_request_v2));
#endif
// Ask them if they support mxp.
add_binary_message(ob, telnet_do_mxp, sizeof(telnet_do_mxp));
// And mssp
add_binary_message(ob, telnet_will_mssp, sizeof(telnet_will_mssp));
// May as well ask for zmp while we're there!
add_binary_message(ob, telnet_will_zmp, sizeof(telnet_will_zmp));
// Also newenv
add_binary_message(ob, telnet_do_newenv, sizeof(telnet_do_newenv));
// gmcp *yawn*
add_binary_message(ob, telnet_do_gmcp, sizeof(telnet_do_gmcp));
}
logon(ob);
debug(connections, ("new_user_handler: end\n"));
set_command_giver(0);
} /* new_user_handler() */
/*
* Return the first command of the next user in sequence that has a complete
* command in their buffer. A command is defined to be a single character
* when SINGLE_CHAR is set, or a newline terminated string otherwise.
*/
static char *get_user_command()
{
static int NextCmdGiver = 0;
int i;
interactive_t *ip;
char *user_command = 0;
/* find and return a user command */
for (i = 0; i < max_users; i++) {
ip = all_users[NextCmdGiver++];
NextCmdGiver %= max_users;
if (!ip || !ip->ob || ip->ob->flags & O_DESTRUCTED)
continue;
/* if we've got text to send, try to flush it, could lose the link here */
if (ip->message_length) {
object_t *ob = ip->ob;
flush_message(ip);
if (!IP_VALID(ip, ob) || (ip->iflags & NET_DEAD))
continue;
}
/* if there's a command in the buffer, pull it out! */
if (ip->iflags & CMD_IN_BUF) {
NextCmdGiver++;
NextCmdGiver %= max_users;
user_command = first_cmd_in_buf(ip);
break;
}
}
/* no command found - return 0 */
if (!user_command)
return 0;
/* got a command - return it and set command_giver */
debug(connections, ("get_user_command: user_command = (%s)\n", user_command));
save_command_giver(ip->ob);
#ifndef GET_CHAR_IS_BUFFERED
if (ip->iflags & NOECHO) {
#else
if ((ip->iflags & NOECHO) && !(ip->iflags & SINGLE_CHAR)) {
#endif
/* must not enable echo before the user input is received */
add_binary_message(command_giver, telnet_no_echo, sizeof(telnet_no_echo));
ip->iflags &= ~NOECHO;
}
ip->last_time = current_time;
return user_command;
} /* get_user_command() */
static int escape_command (interactive_t * ip, char * user_command)
{
if (user_command[0] != '!')
return 0;
#ifdef OLD_ED
if (ip->ed_buffer)
return 1;
#endif
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
if (ip->input_to && ( !(ip->iflags & NOESC) && !(ip->iflags & I_SINGLE_CHAR) ) )
return 1;
#endif
return 0;
}
static void process_input (interactive_t * ip, char * user_command)
{
svalue_t *ret;
if (!(ip->iflags & HAS_PROCESS_INPUT)) {
parse_command(user_command, command_giver);
return;
}
/*
* send a copy of user input back to user object to provide
* support for things like command history and mud shell
* programming languages.
*/
copy_and_push_string(user_command);
ret = apply(APPLY_PROCESS_INPUT, command_giver, 1, ORIGIN_DRIVER);
if (!IP_VALID(ip, command_giver))
return;
if (!ret) {
ip->iflags &= ~HAS_PROCESS_INPUT;
parse_command(user_command, command_giver);
return;
}
#ifndef NO_ADD_ACTION
if (ret->type == T_STRING) {
static char buf[MAX_TEXT];
strncpy(buf, ret->u.string, MAX_TEXT - 1);
parse_command(buf, command_giver);
} else {
if (ret->type != T_NUMBER || !ret->u.number)
parse_command(user_command, command_giver);
}
#endif
}
/*
* This is the user command handler. This function is called when
* a user command needs to be processed.
* This function calls get_user_command() to get a user command.
* One user command is processed per execution of this function.
*/
int process_user_command()
{
char *user_command;
interactive_t *ip=NULL;//for if(ip) below
/*
* WARNING: get_user_command() sets command_giver via
* save_command_giver(), but only when the return is non-zero!
*/
if (!(user_command = get_user_command()))
return 0;
if(command_giver) ip = command_giver->interactive;
current_interactive = command_giver; /* this is yuck phooey, sigh */
if(ip) clear_notify(ip->ob);
update_load_av();
debug(connections, ("process_user_command: command_giver = /%s\n", command_giver->obname));
if(!ip)
goto exit;
user_command = translate_easy(ip->trans->incoming, user_command);
if(ip->iflags & USING_MXP && user_command[0] == ' ' && user_command[1] == '[' && user_command[3] == 'z' ){
svalue_t *ret;
copy_and_push_string(user_command);
ret=apply(APPLY_MXP_TAG, ip->ob, 1, ORIGIN_DRIVER);
if(ret && ret->type==T_NUMBER && ret->u.number){
goto exit;
}
}
if (escape_command(ip, user_command)) {
if (ip->iflags & SINGLE_CHAR) {
/* only 1 char ... switch to line buffer mode */
ip->iflags |= WAS_SINGLE_CHAR;
ip->iflags &= ~SINGLE_CHAR;
#ifdef GET_CHAR_IS_BUFFERED
ip->text_start = ip->text_end = *ip->text = 0;
#endif
set_linemode(ip);
} else {
if (ip->iflags & WAS_SINGLE_CHAR) {
/* we now have a string ... switch back to char mode */
ip->iflags &= ~WAS_SINGLE_CHAR;
ip->iflags |= SINGLE_CHAR;
set_charmode(ip);
if (!IP_VALID(ip, command_giver)) {
goto exit;
}
}
process_input(ip, user_command + 1);
}
goto exit;
}
#ifdef OLD_ED
if (ip->ed_buffer) {
ed_cmd(user_command);
goto exit;
}
#endif
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
if (call_function_interactive(ip, user_command)) {
goto exit;
}
#endif
process_input(ip, user_command);
exit:
/*
* Print a prompt if user is still here.
*/
if (IP_VALID(ip, command_giver))
print_prompt(ip);
current_interactive = 0;
restore_command_giver();
return 1;
}
#define HNAME_BUF_SIZE 200
/*
* This is the hname input data handler. This function is called by the
* master handler when data is pending on the hname socket (addr_server_fd).
*/
static void hname_handler()
{
static char hname_buf[HNAME_BUF_SIZE];
static int hname_buf_pos;
int num_bytes;
num_bytes = HNAME_BUF_SIZE - hname_buf_pos - 1; /* room for nul */
num_bytes = OS_socket_read(addr_server_fd, hname_buf + hname_buf_pos, num_bytes);
if (num_bytes <= 0) {
if (num_bytes == -1) {
#ifdef EWOULDBLOCK
if (socket_errno == EWOULDBLOCK) {
debug(connections, ("hname_handler: read on fd %d: Operation would block.\n",
addr_server_fd));
return;
}
#endif
debug_message("hname_handler: read on fd %d\n", addr_server_fd);
socket_perror("hname_handler: read", 0);
} else {
debug_message("hname_handler: closing address server connection.\n");
}
OS_socket_close(addr_server_fd);
addr_server_fd = -1;
return;
}
hname_buf_pos += num_bytes;
hname_buf[hname_buf_pos] = 0;
debug(connections, ("hname_handler: address server replies: %s", hname_buf));
while (hname_buf_pos) {
char *nl, *pp;
/* if there's no newline, there's more data to come */
if (!(nl = strchr(hname_buf, '\n')))
break;
*nl++ = 0;
if ((pp = strchr(hname_buf, ' ')) != 0) {
*pp++ = 0;
got_addr_number(pp, hname_buf);
if (isdigit(hname_buf[0]) || hname_buf[0] == ':') {
#ifdef IPV6
struct in6_addr addr;
int ret;
if(1 ==(ret = inet_pton(AF_INET6, hname_buf, &addr))) {
if (strcmp(pp, "0") != 0)
add_ip_entry(addr, pp);
}
#else
unsigned long laddr;
if ((laddr = inet_addr(hname_buf)) != INADDR_NONE) {
if (strcmp(pp, "0") != 0)
add_ip_entry(laddr, pp);
}
#endif
}
}
hname_buf_pos -= (nl - hname_buf);
if (hname_buf_pos)
memmove(hname_buf, nl, hname_buf_pos + 1); /* be sure to get the nul */
}
}
/*
* Remove an interactive user immediately.
*/
void remove_interactive (object_t * ob, int dested)
{
int idx;
/* don't have to worry about this dangling, since this is the routine
* that causes this to dangle elsewhere, and we are protected from
* getting called recursively by CLOSING. safe_apply() should be
* used here, since once we start this process we can't back out,
* so jumping out with an error would be bad.
*/
interactive_t *ip = ob->interactive;
if (!ip) return;
if (ip->iflags & CLOSING) {
if (!dested)
debug_message("Double call to remove_interactive()\n");
return;
}
debug(connections, ("Closing connection from %s.\n",
inet_ntoa(ip->addr.sin_addr)));
flush_message(ip);
ip->iflags |= CLOSING;
#ifdef OLD_ED
if (ip->ed_buffer) {
save_ed_buffer(ob);
}
#else
if (ob->flags & O_IN_EDIT) {
object_save_ed_buffer(ob);
ob->flags &= ~O_IN_EDIT;
}
#endif
if (!dested) {
/*
* auto-notification of net death
*/
save_command_giver(ob);
safe_apply(APPLY_NET_DEAD, ob, 0, ORIGIN_DRIVER);
restore_command_giver();
}
#ifndef NO_SNOOP
if (ip->snooped_by) {
ip->snooped_by->flags &= ~O_SNOOP;
ip->snooped_by = 0;
}
#endif
#ifdef HAVE_ZLIB
if (ip->compressed_stream) {
end_compression(ip);
}
#endif
debug(connections, ("remove_interactive: closing fd %d\n", ip->fd));
if (OS_socket_close(ip->fd) == -1) {
socket_perror("remove_interactive: close", 0);
}
#ifdef F_SET_HIDE
if (ob->flags & O_HIDDEN)
num_hidden_users--;
#endif
num_user--;
clear_notify(ip->ob);
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
if (ip->input_to) {
free_object(&ip->input_to->ob, "remove_interactive");
free_sentence(ip->input_to);
if (ip->num_carry > 0)
free_some_svalues(ip->carryover, ip->num_carry);
ip->carryover = NULL;
ip->num_carry = 0;
ip->input_to = 0;
}
#endif
for (idx = 0; idx < max_users; idx++)
if (all_users[idx] == ip) break;
DEBUG_CHECK(idx == max_users, "remove_interactive: could not find and remove user!\n");
FREE(ip->sb_buf);
FREE(ip);
ob->interactive = 0;
all_users[idx] = 0;
free_object(&ob, "remove_interactive");
return;
} /* remove_interactive() */
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
static int call_function_interactive (interactive_t * i, char * str)
{
object_t *ob;
funptr_t *funp;
char *function;
svalue_t *args;
sentence_t *sent;
int num_arg;
#ifdef GET_CHAR_IS_BUFFERED
int was_single = 0;
int was_noecho = 0;
#endif
i->iflags &= ~NOESC;
if (!(sent = i->input_to))
return (0);
/*
* Special feature: input_to() has been called to setup a call to a
* function.
*/
if (sent->ob->flags & O_DESTRUCTED) {
/* Sorry, the object has selfdestructed ! */
free_object(&sent->ob, "call_function_interactive");
free_sentence(sent);
i->input_to = 0;
if (i->num_carry)
free_some_svalues(i->carryover, i->num_carry);
i->carryover = NULL;
i->num_carry = 0;
i->input_to = 0;
if (i->iflags & SINGLE_CHAR) {
/*
* clear single character mode
*/
i->iflags &= ~SINGLE_CHAR;
#ifndef GET_CHAR_IS_BUFFERED
set_linemode(i);
#else
was_single = 1;
if (i->iflags & NOECHO) {
was_noecho = 1;
i->iflags &= ~NOECHO;
}
#endif
}
return (0);
}
/*
* We must all references to input_to fields before the call to apply(),
* because someone might want to set up a new input_to().
*/
/* we put the function on the stack in case of an error */
STACK_INC;
if (sent->flags & V_FUNCTION) {
function = 0;
sp->type = T_FUNCTION;
sp->u.fp = funp = sent->function.f;
funp->hdr.ref++;
} else {
sp->type = T_STRING;
sp->subtype = STRING_SHARED;
sp->u.string = function = sent->function.s;
ref_string(function);
}
ob = sent->ob;
free_object(&sent->ob, "call_function_interactive");
free_sentence(sent);
/*
* If we have args, we have to copy them, so the svalues on the
* interactive struct can be FREEd
*/
num_arg = i->num_carry;
if (num_arg) {
args = i->carryover;
i->num_carry = 0;
i->carryover = NULL;
} else
args = NULL;
i->input_to = 0;
if (i->iflags & SINGLE_CHAR) {
/*
* clear single character mode
*/
i->iflags &= ~SINGLE_CHAR;
#ifndef GET_CHAR_IS_BUFFERED
set_linemode(i);
#else
was_single = 1;
if (i->iflags & NOECHO) {
was_noecho = 1;
i->iflags &= ~NOECHO;
}
#endif
}
copy_and_push_string(str);
/*
* If we have args, we have to push them onto the stack in the order they
* were in when we got them. They will be popped off by the called
* function.
*/
if (args) {
transfer_push_some_svalues(args, num_arg);
FREE(args);
}
/* current_object no longer set */
if (function) {
if (function[0] == APPLY___INIT_SPECIAL_CHAR)
error("Illegal function name.\n");
(void) apply(function, ob, num_arg + 1, ORIGIN_INTERNAL);
} else
call_function_pointer(funp, num_arg + 1);
pop_stack(); /* remove `function' from stack */
#ifdef GET_CHAR_IS_BUFFERED
if (IP_VALID(i, ob)) {
if (was_single && !(i->iflags & SINGLE_CHAR)) {
i->text_start = i->text_end = 0;
i->text[0] = '\0';
i->iflags &= ~CMD_IN_BUF;
set_linemode(i);
}
if (was_noecho && !(i->iflags & NOECHO))
add_binary_message(i->ob, telnet_no_echo, sizeof(telnet_no_echo));
}
#endif
return (1);
} /* call_function_interactive() */
int set_call (object_t * ob, sentence_t * sent, int flags)
{
if (ob == 0 || sent == 0)
return (0);
if (ob->interactive == 0 || ob->interactive->input_to)
return (0);
ob->interactive->input_to = sent;
ob->interactive->iflags |= (flags & (I_NOECHO | I_NOESC | I_SINGLE_CHAR));
if (flags & I_NOECHO)
add_binary_message(ob, telnet_yes_echo, sizeof(telnet_yes_echo));
if (flags & I_SINGLE_CHAR)
set_charmode(ob->interactive);
return (1);
} /* set_call() */
#endif
void set_prompt (const char * str)
{
if (command_giver && command_giver->interactive) {
command_giver->interactive->prompt = str;
}
} /* set_prompt() */
/*
* Print the prompt, but only if input_to not is disabled.
*/
static void print_prompt (interactive_t* ip)
{
object_t *ob = ip->ob;
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
if (ip->input_to == 0) {
#endif
/* give user object a chance to write its own prompt */
if (!(ip->iflags & HAS_WRITE_PROMPT))
tell_object(ip->ob, ip->prompt, strlen(ip->prompt));
#ifdef OLD_ED
else if (ip->ed_buffer)
tell_object(ip->ob, ip->prompt, strlen(ip->prompt));
#endif
else if (!apply(APPLY_WRITE_PROMPT, ip->ob, 0, ORIGIN_DRIVER)) {
if (!IP_VALID(ip, ob)) return;
ip->iflags &= ~HAS_WRITE_PROMPT;
tell_object(ip->ob, ip->prompt, strlen(ip->prompt));
}
#if defined(F_INPUT_TO) || defined(F_GET_CHAR)
}
#endif
if (!IP_VALID(ip, ob)) return;
/*
* Put the IAC GA thing in here... Moved from before writing the prompt;
* vt src says it's a terminator. Should it be inside the no-input_to
* case? We'll see, I guess.
*/
if ((ip->iflags & USING_TELNET) && !(ip->iflags & SUPPRESS_GA))
add_binary_message(command_giver, telnet_ga, sizeof(telnet_ga));
if (!IP_VALID(ip, ob)) return;
} /* print_prompt() */
/*
* Let object 'me' snoop object 'you'. If 'you' is 0, then turn off
* snooping.
*
* This routine is almost identical to the old set_snoop. The main
* difference is that the routine writes nothing to user directly,
* all such communication is taken care of by the mudlib. It communicates
* with master.c in order to find out if the operation is permissble or
* not. The old routine let everyone snoop anyone. This routine also returns
* 0 or 1 depending on success.
*/
#ifndef NO_SNOOP
int new_set_snoop (object_t * by, object_t * victim)
{
interactive_t *ip;
object_t *tmp;
if (by->flags & O_DESTRUCTED)
return 0;
if (victim && (victim->flags & O_DESTRUCTED))
return 0;
if (victim) {
if (!victim->interactive)
error("Second argument of snoop() is not interactive!\n");
ip = victim->interactive;
} else {
/*
* Stop snoop.
*/
if (by->flags & O_SNOOP) {
int i;
for (i = 0; i < max_users; i++) {
if (all_users[i] && all_users[i]->snooped_by == by)
all_users[i]->snooped_by = 0;
}
by->flags &= ~O_SNOOP;
}
return 1;
}
/*
* Protect against snooping loops.
*/
tmp = by;
while (tmp) {
if (tmp == victim)
return 0;
/* the person snooping us, if any */
tmp = (tmp->interactive ? tmp->interactive->snooped_by : 0);
}
/*
* Terminate previous snoop, if any.
*/
if (by->flags & O_SNOOP) {
int i;
for (i = 0; i < max_users; i++) {
if (all_users[i] && all_users[i]->snooped_by == by)
all_users[i]->snooped_by = 0;
}
}
if (ip->snooped_by)
ip->snooped_by->flags &= ~O_SNOOP;
by->flags |= O_SNOOP;
ip->snooped_by = by;
return 1;
} /* set_new_snoop() */
#endif
static void query_addr_name (object_t * ob)
{
static char buf[100];
static char *dbuf = &buf[sizeof(int) + sizeof(int) + sizeof(int)];
int msglen;
int msgtype;
sprintf(dbuf, "%s", query_ip_number(ob));
msglen = sizeof(int) + strlen(dbuf) +1;
msgtype = DATALEN;
memcpy(buf, (char *) &msgtype, sizeof(msgtype));
memcpy(&buf[sizeof(int)], (char *) &msglen, sizeof(msglen));
msgtype = NAMEBYIP;
memcpy(&buf[sizeof(int) + sizeof(int)], (char *) &msgtype, sizeof(msgtype));
debug(connections, ("query_addr_name: sent address server %s\n", dbuf));
if (OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int)) == -1) {
switch (socket_errno) {
case EBADF:
debug_message("Address server has closed connection.\n");
addr_server_fd = -1;
break;
default:
socket_perror("query_addr_name: write", 0);
break;
}
}
} /* query_addr_name() */
#define IPSIZE 200
typedef struct {
char *name;
svalue_t call_back;
object_t *ob_to_call;
} ipnumberentry_t;
static ipnumberentry_t ipnumbertable[IPSIZE];
/*
* Does a call back on the current_object with the function call_back.
*/
int query_addr_number (const char * name, svalue_t * call_back)
{
static char buf[100];
static char *dbuf = &buf[sizeof(int) + sizeof(int) + sizeof(int)];
int msglen;
int msgtype;
int i;
if ((addr_server_fd < 0) || (strlen(name) >=
100 - (sizeof(msgtype) + sizeof(msglen) + sizeof(int)))) {
share_and_push_string(name);
push_undefined();
if (call_back->type == T_STRING)
apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL);
else
call_function_pointer(call_back->u.fp, 2);
return 0;
}
strcpy(dbuf, name);
msglen = sizeof(int) + strlen(name) +1;
msgtype = DATALEN;
memcpy(buf, (char *) &msgtype, sizeof(msgtype));
memcpy(&buf[sizeof(int)], (char *) &msglen, sizeof(msglen));
msgtype = NAMEBYIP;
for (i = 0; i < strlen(name); i++){
if (isalpha(name[i])) {
msgtype = IPBYNAME;
break;
}
}
memcpy(&buf[sizeof(int) + sizeof(int)], (char *) &msgtype, sizeof(msgtype));
debug(connections, ("query_addr_number: sent address server %s\n", dbuf));
if (addr_server_fd && OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int)) == -1) {
switch (socket_errno) {
case EBADF:
debug_message("Address server has closed connection.\n");
addr_server_fd = -1;
break;
default:
socket_perror("query_addr_name: write", 0);
break;
}
share_and_push_string(name);
push_undefined();
if (call_back->type == T_STRING)
apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL);
else
call_function_pointer(call_back->u.fp, 2);
return 0;
} else {
int i;
/* We put ourselves into the pending name lookup entry table */
/* Find the first free entry */
for (i = 0; i < IPSIZE && ipnumbertable[i].name; i++)
;
if (i == IPSIZE) {
/* We need to error... */
share_and_push_string(name);
push_undefined();
if (call_back->type == T_STRING)
apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL);
else
call_function_pointer(call_back->u.fp, 2);
return 0;
}
/* Create our entry... */
ipnumbertable[i].name = make_shared_string(name);
assign_svalue_no_free(&ipnumbertable[i].call_back, call_back);
ipnumbertable[i].ob_to_call = current_object;
add_ref(current_object, "query_addr_number: ");
return i + 1;
}
} /* query_addr_number() */
static void got_addr_number (char * number, char * name)
{
int i;
char *theName, *theNumber;
/* First remove all the dested ones... */
for (i = 0; i < IPSIZE; i++)
if (ipnumbertable[i].name
&& ipnumbertable[i].ob_to_call->flags & O_DESTRUCTED) {
free_svalue(&ipnumbertable[i].call_back, "got_addr_number");
free_string(ipnumbertable[i].name);
free_object(&ipnumbertable[i].ob_to_call, "got_addr_number: ");
ipnumbertable[i].name = NULL;
}
for (i = 0; i < IPSIZE; i++) {
if (ipnumbertable[i].name && strcmp(name, ipnumbertable[i].name)== 0) {
/* Found one, do the call back... */
theName = ipnumbertable[i].name;
theNumber = number;
if (uisdigit(theName[0])) {
char *tmp;
tmp = theName;
theName = theNumber;
theNumber = tmp;
}
if (strcmp(theName, "0")) {
share_and_push_string(theName);
} else {
push_undefined();
}
if (strcmp(theNumber, "0")) {
share_and_push_string(theNumber);
} else {
push_undefined();
}
push_number(i + 1);
if (ipnumbertable[i].call_back.type == T_STRING)
safe_apply(ipnumbertable[i].call_back.u.string,
ipnumbertable[i].ob_to_call,
3, ORIGIN_INTERNAL);
else
safe_call_function_pointer(ipnumbertable[i].call_back.u.fp, 3);
free_svalue(&ipnumbertable[i].call_back, "got_addr_number");
free_string(ipnumbertable[i].name);
free_object(&ipnumbertable[i].ob_to_call, "got_addr_number: ");
ipnumbertable[i].name = NULL;
}
}
} /* got_addr_number() */
#undef IPSIZE
#define IPSIZE 200
typedef struct {
#ifdef IPV6
struct in6_addr addr;
#else
long addr;
#endif
char *name;
} ipentry_t;
static ipentry_t iptable[IPSIZE];
static int ipcur;
#ifdef DEBUGMALLOC_EXTENSIONS
void mark_iptable() {
int i;
for (i=0; i < IPSIZE; i++)
if (iptable[i].name)
EXTRA_REF(BLOCK(iptable[i].name))++;
}
#endif
#ifdef IPV6
char ipv6addr[INET6_ADDRSTRLEN];
#endif
char *query_ip_name (object_t * ob)
{
int i;
if (ob == 0)
ob = command_giver;
if (!ob || ob->interactive == 0)
return NULL;
#ifdef IPV6
for (i = 0; i < IPSIZE; i++) {
if (!memcmp(&iptable[i].addr, &ob->interactive->addr.sin6_addr, sizeof(ob->interactive->addr.sin6_addr)) &&
iptable[i].name)
return (iptable[i].name);
}
inet_ntop(AF_INET6, &ob->interactive->addr.sin6_addr, ipv6addr, INET6_ADDRSTRLEN);
return ipv6addr;
#else
for (i = 0; i < IPSIZE; i++) {
if (iptable[i].addr == ob->interactive->addr.sin_addr.s_addr &&
iptable[i].name)
return (iptable[i].name);
}
return (inet_ntoa(ob->interactive->addr.sin_addr));
#endif
}
#ifdef IPV6
static void add_ip_entry (struct in6_addr addr, char * name)
#else
static void add_ip_entry (long addr, char * name)
#endif
{
int i;
for (i = 0; i < IPSIZE; i++) {
if (!memcmp(&iptable[i].addr, &addr, sizeof(addr)))
return;
}
iptable[ipcur].addr = addr;
if (iptable[ipcur].name)
free_string(iptable[ipcur].name);
iptable[ipcur].name = make_shared_string(name);
ipcur = (ipcur + 1) % IPSIZE;
}
const char *query_ip_number (object_t * ob)
{
if (ob == 0)
ob = command_giver;
if (!ob || ob->interactive == 0)
return 0;
#ifdef IPV6
inet_ntop(AF_INET6, &ob->interactive->addr.sin6_addr, ipv6addr, INET6_ADDRSTRLEN);
return &ipv6addr[0];
#else
return (inet_ntoa(ob->interactive->addr.sin_addr));
#endif
}
#ifndef 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 *inet_ntoa (struct in_addr ad)
{
u_long s_ad;
int a, b, c, d;
static char addr[20]; /* 16 + 1 should be enough */
s_ad = ad.s_addr;
d = s_ad % 256;
s_ad /= 256;
c = s_ad % 256;
s_ad /= 256;
b = s_ad % 256;
a = s_ad / 256;
sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
return (addr);
}
#endif /* INET_NTOA_OK */
char *query_host_name()
{
static char name[400];
gethostname(name, sizeof(name));
name[sizeof(name) - 1] = '\0'; /* Just to make sure */
return (name);
} /* query_host_name() */
#ifndef NO_SNOOP
object_t *query_snoop (object_t * ob)
{
if (!ob->interactive)
return 0;
return ob->interactive->snooped_by;
} /* query_snoop() */
object_t *query_snooping (object_t * ob)
{
int i;
if (!(ob->flags & O_SNOOP)) return 0;
for (i = 0; i < max_users; i++) {
if (all_users[i] && all_users[i]->snooped_by == ob)
return all_users[i]->ob;
}
fatal("couldn't find snoop target.\n");
return 0;
} /* query_snooping() */
#endif
int query_idle (object_t * ob)
{
if (!ob->interactive)
error("query_idle() of non-interactive object.\n");
return (current_time - ob->interactive->last_time);
} /* query_idle() */
#ifdef F_EXEC
int replace_interactive (object_t * ob, object_t * obfrom)
{
if (ob->interactive) {
error("Bad argument 1 to exec()\n");
}
if (!obfrom->interactive) {
error("Bad argument 2 to exec()\n");
}
#ifdef F_SET_HIDE
if ((ob->flags & O_HIDDEN) != (obfrom->flags & O_HIDDEN)) {
if (ob->flags & O_HIDDEN) {
num_hidden_users++;
} else {
num_hidden_users--;
}
}
#endif
ob->interactive = obfrom->interactive;
/*
* assume the existance of write_prompt and process_input in user.c until
* proven wrong (after trying to call them).
*/
ob->interactive->iflags |= (HAS_WRITE_PROMPT | HAS_PROCESS_INPUT);
obfrom->interactive = 0;
ob->interactive->ob = ob;
ob->flags |= O_ONCE_INTERACTIVE;
obfrom->flags &= ~O_ONCE_INTERACTIVE;
add_ref(ob, "exec");
if (obfrom == command_giver) {
set_command_giver(ob);
}
free_object(&obfrom, "exec");
return (1);
} /* replace_interactive() */
#endif
void outbuf_zero (outbuffer_t * outbuf) {
outbuf->real_size = 0;
outbuf->buffer = 0;
}
int outbuf_extend (outbuffer_t * outbuf, int l)
{
int limit;
DEBUG_CHECK(l < 0, "Negative length passed to outbuf_extend.\n");
l = (l > MAX_STRING_LENGTH ? MAX_STRING_LENGTH : l);
if (outbuf->buffer) {
limit = MSTR_SIZE(outbuf->buffer);
if (outbuf->real_size + l > limit) {
if (outbuf->real_size == MAX_STRING_LENGTH) return 0; /* TRUNCATED */
/* assume it's going to grow some more */
limit = (outbuf->real_size + l) * 2;
if (limit > MAX_STRING_LENGTH) {
limit = MAX_STRING_LENGTH;
outbuf->buffer = extend_string(outbuf->buffer, limit);
return limit - outbuf->real_size;
}
outbuf->buffer = extend_string(outbuf->buffer, limit);
}
} else {
outbuf->buffer = new_string(l, "outbuf_extend");
outbuf->real_size = 0;
}
return l;
}
void outbuf_add (outbuffer_t * outbuf, const char * str)
{
int l, limit;
if (!outbuf) return;
l = strlen(str);
if ((limit = outbuf_extend(outbuf, l)) > 0) {
strncpy(outbuf->buffer + outbuf->real_size, str, limit);
outbuf->real_size += (l > limit ? limit : l);
*(outbuf->buffer + outbuf->real_size) = 0;
}
}
void outbuf_addchar (outbuffer_t * outbuf, char c)
{
if(outbuf && (outbuf_extend(outbuf, 1) > 0)) {
*(outbuf->buffer + outbuf->real_size++) = c;
*(outbuf->buffer + outbuf->real_size) = 0;
}
}
void outbuf_addv (outbuffer_t *outbuf, const char *format, ...)
{
char buf[LARGEST_PRINTABLE_STRING + 1];
va_list args;
V_START(args, format);
V_VAR(outbuffer_t *, outbuf, args);
V_VAR(char *, format, args);
vsnprintf(buf, LARGEST_PRINTABLE_STRING, format, args);
va_end(args);
if (!outbuf) return;
outbuf_add(outbuf, buf);
}
void outbuf_fix (outbuffer_t * outbuf) {
if (outbuf && outbuf->buffer)
outbuf->buffer = extend_string(outbuf->buffer, outbuf->real_size);
}
void outbuf_push (outbuffer_t * outbuf) {
STACK_INC;
sp->type = T_STRING;
if (outbuf && outbuf->buffer) {
outbuf->buffer = extend_string(outbuf->buffer, outbuf->real_size);
sp->subtype = STRING_MALLOC;
sp->u.string = outbuf->buffer;
} else {
sp->subtype = STRING_CONSTANT;
sp->u.string = "";
}
}
#ifdef HAVE_ZLIB
void* zlib_alloc(void* opaque, unsigned int items, unsigned int size) {
return CALLOC(items, size);
}
void zlib_free(void* opaque, void* address) {
FREE(address);
}
static void end_compression (interactive_t *ip) {
unsigned char dummy[1];
if (!ip->compressed_stream) {
return ;
}
ip->compressed_stream->avail_in = 0;
ip->compressed_stream->next_in = dummy;
if (deflate(ip->compressed_stream, Z_FINISH) != Z_STREAM_END) {
}
deflateEnd(ip->compressed_stream);
FREE(ip->compressed_stream);
ip->compressed_stream = NULL;
}
static void start_compression (interactive_t *ip) {
z_stream* zcompress;
if (ip->compressed_stream) {
return ;
}
zcompress = (z_stream *) DXALLOC(sizeof(z_stream), TAG_INTERACTIVE,
"start_compression");
zcompress->next_in = NULL;
zcompress->avail_in = 0;
zcompress->next_out = ip->compress_buf;
zcompress->avail_out = COMPRESS_BUF_SIZE;
zcompress->zalloc = zlib_alloc;
zcompress->zfree = zlib_free;
zcompress->opaque = NULL;
if (deflateInit(zcompress, 9) != Z_OK) {
FREE(zcompress);
fprintf(stderr, "Compression failed.\n");
return ;
}
// Ok, compressing.
ip->compressed_stream = zcompress;
}
static int flush_compressed_output (interactive_t *ip) {
int iStart, nBlock, nWrite, len;
z_stream* zcompress;
int ret = 1;
if (!ip->compressed_stream) {
return ret;
}
zcompress = ip->compressed_stream;
/* Try to write out some data.. */
len = zcompress->next_out - ip->compress_buf;
if (len > 0) {
/* we have some data to write */
nWrite = 0;
for (iStart = 0; iStart < len; iStart += nWrite)
{
if (len - iStart < 4096) {
nBlock =len - iStart;
} else {
nBlock = 4096;
}
nWrite = send(ip->fd, &ip->compress_buf[iStart], nBlock,
ip->out_of_band);
if (nWrite < 0) {
fprintf(stderr, "Error sending compressed data (%d)\n",
errno);
if (errno == EAGAIN
#ifndef WIN32
|| errno == ENOSR
#endif
) {
ret = 2;
break;
}
return FALSE; /* write error */
}
if (nWrite <= 0) {
break;
}
}
if (iStart) {
/* We wrote "iStart" bytes */
if (iStart < len) {
memmove(ip->compress_buf, ip->compress_buf+iStart, len -
iStart);
}
zcompress->next_out = ip->compress_buf + len - iStart;
}
}
return ret;
}
static int send_compressed (interactive_t *ip, unsigned char* data, int length) {
z_stream* zcompress;
int wr = 1;
int first = 1;
zcompress = ip->compressed_stream;
zcompress->next_in = data;
zcompress->avail_in = length;
while (zcompress->avail_in && (wr == 1 || first)) {
if(wr == 2)
first = 0;
zcompress->avail_out = COMPRESS_BUF_SIZE - (zcompress->next_out -
ip->compress_buf);
if (zcompress->avail_out) {
deflate(zcompress, Z_SYNC_FLUSH);
}
if(!( wr = flush_compressed_output(ip)))
return 0;
}
return length;
}
#endif
#ifdef F_ACT_MXP
void f_act_mxp(){
add_binary_message(current_object, telnet_will_mxp, sizeof(telnet_will_mxp));
}
#endif
#ifdef F_SEND_ZMP
void f_send_zmp(){
add_binary_message(current_object, telnet_start_zmp, sizeof(telnet_start_zmp));
add_binary_message(current_object, (const unsigned char *)(sp-1)->u.string, strlen((sp-1)->u.string));
int i;
unsigned char zero = 0;
for(i=0; i<sp->u.arr->size; i++){
if(sp->u.arr->item[i].type == T_STRING){
add_binary_message(current_object, &zero, 1);
add_binary_message(current_object, (const unsigned char *)sp->u.arr->item[i].u.string, strlen(sp->u.arr->item[i].u.string));
}
}
add_binary_message(current_object, &zero, 1);
add_binary_message(current_object, telnet_end_sub, sizeof(telnet_end_sub));
pop_2_elems();
}
#endif
#ifdef F_SEND_GMCP
void f_send_gmcp(){
add_binary_message(current_object, telnet_start_gmcp, sizeof(telnet_start_gmcp));
add_binary_message(current_object, (const unsigned char *)(sp->u.string), strlen(sp->u.string));
add_binary_message(current_object, telnet_end_sub, sizeof(telnet_end_sub));
}
#endif
#ifdef F_REQUEST_TERM_TYPE
void f_request_term_type(){
add_binary_message(command_giver, telnet_term_query, sizeof(telnet_term_query));
}
#endif
#ifdef F_START_REQUEST_TERM_TYPE
void f_start_request_term_type(){
add_binary_message(command_giver, telnet_do_ttype, sizeof(telnet_do_ttype));
}
#endif
#ifdef F_REQUEST_TERM_SIZE
void f_request_term_size(){
if((st_num_arg == 1) && (sp->u.number == 0))
add_binary_message(command_giver, telnet_dont_naws,
sizeof(telnet_dont_naws));
else
add_binary_message(command_giver, telnet_do_naws, sizeof(telnet_do_naws));
if(st_num_arg == 1)
sp--;
}
#endif