/*
* ipc.c -- inter-process communication
* Part of the contents of this file was originally copied from TinyTalk.
*
* PMF -- Padrone's MudFrontend, a frontend for (maybe mostly LP-)mud
* Thomas Padron-McCarthy (Email: padrone@lysator.liu.se), 1990, 1991
* Share and enjoy, but be nice: don't steal my program! Hugo is watching!
* This file latest updated: May 25, 1992
*
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include "safe_malloc.h"
#include "pmf.h"
#include "config.h"
#include "globals.h"
#ifndef FD_ZERO /* For BSD 4.2 systems */
#define DESCR_MASK int
#define FD_ZERO(p) (*p = 0)
#define FD_SET(n,p) (*p |= (1<<(n)))
#define FD_ISSET(n,p) (*p & (1<<(n)))
#else /* FD_ZERO */ /* For BSD 4.3 systems */
#define DESCR_MASK fd_set
#endif /* FD_ZERO */
extern char *get_now_date_string();
extern int errno;
static struct in_addr host_address;
static struct sockaddr_in socket_address;
static int mud_socket;
/* This is a character that we read "accidentaly" when checking the socket.
* The value 0 means empty, else it will be handled by the next call to
* getmud that returns some more characters.
*/
static int a_character_that_we_read = 0;
/*---------------------------------------------------------------------------*/
/* Get the address (in the internal format) to the mud host. */
static int get_host_address(name, addr)
char *name;
struct in_addr *addr;
{
if (*name == '\0') {
fatal("This cannot happen: No host address specified.");
}
else if (isdigit(name[0])) {
/* Numerical address, e g "130.236.254.13" */
union {
/* Some people have expressed disapproval of the the inetaddr routine. */
long signed_thingy;
unsigned long unsigned_thingy;
} thingy;
addr->s_addr = (unsigned long)inet_addr(name);
thingy.unsigned_thingy = addr->s_addr;
if (thingy.signed_thingy == -1)
fatal("Couldn't find host '%s'.", name);
}
else {
/* A name address, t ex "nanny.lysator.liu.se" */
struct hostent *the_host_entry_p;
if ((the_host_entry_p = gethostbyname(name)) == NULL)
fatal("Couldn't find host '%s'.", name);
bcopy(the_host_entry_p->h_addr, (char *)addr, sizeof(struct in_addr));
}
return 0;
} /* get_host_address */
/* Create a socket and connect it to mud. */
int connect_to_mud(host_string, port_string)
char *host_string, *port_string;
{
if (get_host_address(host_string, &host_address) != 0)
fatal("connect_to_mud: This shouldn't happen.\n");
socket_address.sin_addr.s_addr = host_address.s_addr;
socket_address.sin_family = AF_INET;
socket_address.sin_port = htons(atoi(port_string));
if ((mud_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
fatal("Couldn't open socket.");
if (connect(mud_socket, (struct sockaddr *)&socket_address,
sizeof(struct sockaddr_in)) < 0) {
fatal("Couldn't connect to socket.");
}
#ifdef FNDELAY
fcntl(mud_socket, F_SETFL, FNDELAY);
#endif
set_variable("host", host_string);
set_variable("port", port_string);
connected = 1;
return 0;
} /* connect_to_mud */
int disconnect()
{
connected = 0;
return close(mud_socket);
}
/* Is there anyhting to read from the socket? */
static int output_from_mud_is_available()
{
long nr_to_read;
int ioctl_result, read_result;
char dummy_buf[4];
ioctl_result = ioctl(mud_socket, FIONREAD, &nr_to_read);
if (ioctl_result)
fatal("First ioctl FIONREAD for MUD socket returned %d, nr_to_read = %ld.",
ioctl_result, nr_to_read);
else if (nr_to_read)
return (int)nr_to_read;
#ifdef FNDELAY
else {
/* No text from mud waiting to be read. Check that the socket
* is still open.
* Pell says: Om errno {r EWOULDBLOCK s} {r allt ok. Annars eof. Eller?
*/
read_result = read (mud_socket, dummy_buf, 1);
if (read_result == -1 && errno == EWOULDBLOCK)
return NULL; /* Normal! */
else if (read_result == 0)
say_goodbye_and_exit(0);
else {
/* Sigh! Some text arrived between the ioctl call and the read!
* Save this accidentaly read character, and see how many more
* characters the idiots sent us.
*/
a_character_that_we_read = dummy_buf[0];
ioctl_result = ioctl(mud_socket, FIONREAD, &nr_to_read);
if (ioctl_result)
fatal("Second ioctl FIONREAD for MUD socket returned %d, nr_to_read = %ld.",
ioctl_result, nr_to_read);
else
return (int)nr_to_read;
}
}
#else
else
return 0;
#endif
} /* output_from_mud_is_available */
/* Has the player typed anything on the terminal? */
int input_from_player_is_available()
{
long nr_to_read;
int result;
if (stdin->_cnt > 0)
return 1;
else if (result = ioctl (fileno(stdin), FIONREAD, &nr_to_read)) /* =, not == */
fatal("ioctl FIONREAD for stdin returns %d, nr_to_read=%ld.", result, nr_to_read);
else
return (int)nr_to_read;
} /* input_from_player_is_available */
/*---------------------------------------------------------------------------*/
/* Send a command to the Mud process. Add a newline. */
send_to_mud(str)
char *str;
{
int nr_to_send, nr_actually_sent;
char *buffer;
nr_to_send = strlen(str);
/* All this work just to add a newline: */
buffer = safe_malloc(nr_to_send + 3);
strcpy(buffer, str);
buffer[nr_to_send] = '\r';
++nr_to_send;
buffer[nr_to_send] = '\n';
++nr_to_send;
buffer[nr_to_send] = '\0';
IPC_DEBUG(("send_to_mud: sending (%d chars): %s", nr_to_send, buffer));
nr_actually_sent = write(mud_socket, buffer, nr_to_send);
if (log_file != NULL) {
if (debug_log)
fprintf(log_file, "Sent (%d chars) at %8.8s:\n%s",
nr_actually_sent, get_now_date_string() + 11, buffer);
else
fprintf(log_file, "%s", buffer);
fflush(log_file);
}
if (nr_actually_sent != nr_to_send)
fatal("Wrote %d instead of %d characters to MUD socket in sendmud.",
nr_actually_sent, nr_to_send);
/* When typing the password, echo is turned off. Turn it back on! */
if (echo_is_off) { /* A bit kludgy. */
ldisplay("\n");
echo_on(); /* This is done using the telnet protocol too! */
}
safe_free(buffer);
} /* send_to_mud */
/* Find the length of the text up to next null byte or including next newline. */
static int get_line_length(cp)
register char *cp;
{
register int len;
len = 0;
while (*cp && *cp != '\n') {
++cp;
++len;
}
if (*cp == '\n')
++len;
return len;
} /* get_line_length */
/* Get ONE line from mud. */
char *get_line_from_mud()
{
static char *rec_buffer = NULL, *current_bufp, *after_buffer;
char *the_line;
int buffer_size, nr_to_read, nr_actually_read, line_length;
/* Just to be sure */
if (rec_buffer && *current_bufp == '\0') {
message("Problem in get_line_from_mud: Null byte in buffer changed to '?'.");
*current_bufp = '?';
}
buffer_size = 0;
if (!rec_buffer) {
/* The buffer is empty. Maybe we can get more from the socket? */
if (!connected)
return NULL; /* In this case, obviously we cannot! */
nr_to_read = output_from_mud_is_available();
/* Note: Now, a_character_that_we_read might be set! */
if (nr_to_read) {
/* Wow! There IS something to read from the socket! */
buffer_size = nr_to_read + 1; /* The null byte too. */
rec_buffer = safe_malloc(buffer_size);
nr_actually_read = read(mud_socket, rec_buffer, nr_to_read);
IPC_DEBUG(("read(mud_socket, rec_buffer, nr_to_read) returned %d", nr_actually_read));
rec_buffer[nr_actually_read] = '\0';
if (log_file != NULL) {
if (debug_log)
fprintf(log_file, "Received (%d chars) at %8.8s:\n%s",
nr_actually_read, get_now_date_string() + 11, rec_buffer);
else
fprintf(log_file, "%s", rec_buffer);
fflush(log_file);
}
if (nr_actually_read != nr_to_read)
fatal("Read %d instead of %d characters from MUD socket in get_line_from_mud.",
nr_actually_read, nr_to_read);
}
if (a_character_that_we_read) {
/* One more character to take care of. Put it first in the buffer. */
if (!buffer_size)
buffer_size = 1;
++buffer_size;
if (rec_buffer)
rec_buffer = safe_realloc(rec_buffer, buffer_size);
else
rec_buffer = safe_malloc(buffer_size);
copy_bytes_down(rec_buffer + 1, rec_buffer, buffer_size - 1);
rec_buffer[0] = a_character_that_we_read;
a_character_that_we_read = 0;
}
current_bufp = rec_buffer;
after_buffer = rec_buffer + buffer_size - 1;
}
if (!rec_buffer)
return NULL;
/* If this text was just received: remove telnet control sequences! */
if (current_bufp == rec_buffer) {
char *cp;
/* -- for efficiency, but not needed --
while (*(unsigned char *)current_bufp == 255) {
IPC_DEBUG(("Got three telnet control characters (1) in get_line_from_mud"));
telnet_protocol((unsigned char)current_bufp[0],
(unsigned char)current_bufp[1],
(unsigned char)current_bufp[2]);
current_bufp += 3;
}
*/
cp = current_bufp;
while (cp < after_buffer) {
if (*(unsigned char *)cp == 255) {
IPC_DEBUG(("Got three telnet control characters (2) in get_line_from_mud"));
telnet_protocol((unsigned char)cp[0], (unsigned char)cp[1], (unsigned char)cp[2]);
after_buffer -= 3;
if (after_buffer + 3 > cp)
/* Yes, NannyMud once sent me \377 as the last byte... */
copy_bytes_up(cp, cp+3, (after_buffer - cp) + 1);
/* +1: The null-byte too! */
else {
*cp = '\0';
break; /* End loop */
}
}
else
++cp;
}
if (current_bufp == after_buffer) {
safe_free(rec_buffer);
rec_buffer = NULL;
return NULL;
}
}
/* Now, we have some text to take care of. It was either waiting for us
* in the buffer, or the buffer was empty and we found some text to read
* from the mud game.
*/
line_length = get_line_length(current_bufp);
the_line = safe_malloc(line_length + 1);
strncpy(the_line, current_bufp, line_length);
the_line[line_length] = '\0'; /* After the newline (if there is one) */
current_bufp += line_length;
if (current_bufp >= after_buffer) {
IPC_DEBUG(("get_line_from_mud: freeing the buffer"));
safe_free(rec_buffer);
rec_buffer = NULL;
}
/* If the line ends with CR + LF and not only LF, remove that CR! */
if (the_line[line_length - 2] == '\015' && the_line[line_length - 1] == '\012') {
the_line[line_length - 2] = '\012';
the_line[line_length - 1] = '\0';
}
return the_line;
} /* get_line_from_mud */
/* This function takes the three bytes from a telnet command,
* and implements a very limited telnet protocol.
*/
telnet_protocol(one, two, three)
unsigned int one, two, three;
{
unsigned char reply[3];
int nr_actually_sent;
IPC_DEBUG(("telnet_protocol(%d, %d, %d)", one, two, three));
ASSERT(one == 255);
reply[0] = 255; /* The code for IAC, i. e. "interpret as command:" */
reply[2] = three; /* Usually we answer with the same option. */
/* 251 is the code for WILL, i. e. "I will use option" */
/* 252 is the code for WONT, i. e. "I won't use option" */
/* 253 is the code for DO, i. e. "please, you use option" */
/* 1 is the option code for ECHO */
if (two == 251 && three == 1) {
/* Got "I will use option echo" -- reply "I won't use option echo" */
reply[1] = 252; /* The code for WONT, i. e. "I won't use option" */
echo_off();
}
else if (two == 252 && three == 1) {
/* Got "I will not use option echo" -- reply "I will use option echo" */
reply[1] = 251; /* The code for WILL, i. e. "I will use option" */
echo_on();
}
else {
IPC_DEBUG(("telnet_protocol, not responding to that command"));
return;
}
IPC_DEBUG(("telnet_protocol responding: %d, %d, %d", reply[0], reply[1], reply[2]));
nr_actually_sent = write(mud_socket, reply, 3);
if (log_file != NULL) {
fprintf(log_file, "Sent (%d chars):\n%s", nr_actually_sent, reply);
fflush(log_file);
}
if (nr_actually_sent != 3)
fatal("Wrote %d instead of %d characters to MUD socket in telnet_protocol.",
nr_actually_sent, 3);
} /* telnet_protocol */
syntax highlighted by Code2HTML, v. 0.9.1