/*
* comm.c -- communications functions and more.
* Dwayne Fontenot (Jacques@TMI)
*/
#include "config.h"
#ifdef SunOS_5
#include <stdlib.h>
#endif
#if defined(__386BSD__) || defined(SunOS_5)
#include <unistd.h>
#endif
#ifndef LATTICE
#include <varargs.h>
#endif
#include <sys/types.h>
#include <sys/time.h>
#ifndef LATTICE
#include <sys/ioctl.h>
#endif
#ifdef __386BSD__
#include <sys/param.h>
#endif
#define TELOPTS
#ifndef LATTICE
#include <arpa/telnet.h>
#include <netdb.h>
#include <fcntl.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#ifndef LATTICE
#include <memory.h>
#else
#include "amiga.h"
#define read(s,t,l) read_socket(s,t,l)
#define write(s,t,l) write_socket(s,t,l)
#define close(s) close_socket(s)
#endif
#include <setjmp.h>
#include "lint.h"
#include "interpret.h"
#include "comm.h"
#include "socket_efuns.h"
#include "object.h"
#include "sent.h"
#include "debug.h"
/*
* external function prototypes.
*/
extern char *xalloc(), *string_copy(), *unshared_str_copy();
extern int parse_command();
extern void call_heart_beat();
extern void debug_message(), fatal(), free_sentence();
#ifdef ED
extern void save_ed_buffer();
#endif
#ifdef ACCESS_RESTRICTED
extern void release_host_access();
void *allow_host_access();
#else
int allow_host_access();
#endif /* ACCESS_RESTRICTED */
int total_users = 0;
/*
* local function prototypes.
*/
void init_user_conn();
void ipc_remove();
void init_addr_server();
void add_message();
int flush_message();
static int copy_chars();
void sigpipe_handler();
void sigalrm_handler();
int process_user_command();
void hname_handler();
void get_user_data();
char *get_user_command();
char *first_cmd_in_buf PROT((struct interactive *));
int cmd_in_buf PROT((struct interactive *));
void next_cmd_in_buf PROT((struct interactive *));
void remove_interactive();
int call_function_interactive();
int set_call();
void set_prompt PROT((char *));
void print_prompt();
int new_set_snoop();
void telnet_neg PROT((char *, char *));
void query_addr_name PROT((struct object *));
void got_addr_number PROT((char *, char *));
char *query_ip_name();
static void add_ip_entry PROT((long, char *));
char *query_ip_number PROT((struct object *));
char *query_host_name();
struct object *query_snoop();
int query_idle();
void notify_no_command();
void clear_notify();
void set_notify_fail_message();
int replace_interactive();
/*
* external variables.
*/
extern int port_number;
extern int errno;
extern int d_flag;
extern int current_time;
extern struct object *command_giver, *current_interactive;
#ifdef SOCKET_EFUNS
extern struct lpc_socket lpc_socks[];
#endif
extern int heart_beat_flag;
extern char *default_fail_message;
#ifdef RECEIVE_SNOOP
void receive_snoop PROT((char *, struct object *ob));
#endif
/*
* public local variables.
*/
fd_set readmask, writemask;
int num_user;
int num_hidden; /* 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. */
#ifdef COMM_STAT
int add_message_calls=0;
int inet_packets=0;
int inet_volume=0;
#endif /* COMM_STAT */
struct interactive *all_users[MAX_USERS];
/*
* private local variables.
*/
static int new_user_fd;
static int addr_server_fd = 0;
#ifdef RECEIVE_SNOOP
void
receive_snoop(buf, ob)
char *buf;
struct object *ob;
{
push_constant_string(buf);
apply("receive_snoop", ob, 1);
}
#endif
/*
* Initialize new user connection socket.
*/
void init_user_conn()
{
struct sockaddr_in sin;
int sin_len;
int optval;
/*
* create socket of proper type.
*/
if((new_user_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
perror("init_user_conn: socket");
exit(1);
}
/*
* enable local address reuse.
*/
optval = 1;
if(setsockopt(new_user_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&optval,
sizeof(optval)) == -1){
perror("init_user_conn: setsockopt");
exit(2);
}
/*
* fill in socket address information.
*/
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons((u_short)port_number);
/*
* bind name to socket.
*/
if(bind(new_user_fd,(struct sockaddr *)&sin,sizeof(sin)) == -1){
perror("init_user_conn: bind");
exit(3);
}
/*
* get socket name.
*/
sin_len = sizeof(sin);
if(getsockname(new_user_fd,(struct sockaddr *)&sin,&sin_len) == -1){
perror("init_user_conn: getsockname");
exit(4);
}
/*
* register signal handler for SIGPIPE.
*/
#ifndef LATTICE
if(signal(SIGPIPE,sigpipe_handler) == SIGNAL_ERROR){
perror("init_user_conn: signal SIGPIPE");
exit(5);
}
#endif
/*
* set socket non-blocking,
*/
if(set_socket_nonblocking(new_user_fd, 1) == -1){
perror("init_user_conn: set_socket_nonblocking 1");
exit(8);
}
/*
* listen on socket for connections.
*/
if(listen(new_user_fd,SOMAXCONN) == -1){
perror("init_user_conn: listen");
exit(10);
}
}
/*
* Shut down new user accept file descriptor.
*/
void ipc_remove() {
fprintf(stderr,"Shutting down new user conn...\n");
/*
* disallow further sends or receives on socket.
*/
#if !defined(ultrix)
if(shutdown(new_user_fd,2) == -1){
perror("ipc_remove: shutdown");
}
#else /* ultrix */
shutdown(new_user_fd,2);
#endif /* !defined(ultrix) */
if(close(new_user_fd) == -1){
perror("ipc_remove: close");
}
printf("closed new_user_port\n");
}
void init_addr_server(hostname,addr_server_port)
char *hostname;
int addr_server_port;
{
struct sockaddr_in server;
struct hostent *hp;
int server_fd;
int optval;
/*
* get network host data for hostname.
*/
hp = gethostbyname(hostname);
if(hp == NULL){
perror("init_addr_server: gethostbyname");
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);
if(server_fd < 0){ /* problem opening socket */
perror("init_addr_server: socket");
return;
}
/*
* enable local address reuse.
*/
optval = 1;
if(setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&optval,
sizeof(optval)) == -1){
perror("init_addr_server: setsockopt");
return;
}
/*
* connect socket to server address.
*/
if(connect(server_fd, (struct sockaddr *)&server, sizeof(server)) == -1){
perror("init_addr_server: connect");
close(server_fd);
return;
}
addr_server_fd = server_fd;
fprintf(stderr,"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){
perror("init_addr_server: set_socket_nonblocking 1");
return;
}
}
/*
* Send a message to an interactive object. If that object is shadowed,
* special handling is done.
*/
void
add_message(va_alist)
va_dcl
{
va_list args;
char *format;
struct interactive *ip;
char *cp, new_string_data[LARGEST_PRINTABLE_STRING];
struct object *save_command_giver;
va_start(args);
format = va_arg(args, char *);
/*
* if command_giver->interactive is not valid, write message on stderr.
* (maybe)
*/
if ((command_giver == 0) || (command_giver->flags & O_DESTRUCTED)
|| (command_giver->interactive == 0)
|| command_giver->interactive->net_dead
|| command_giver->interactive->closing)
{
#ifdef NONINTERACTIVE_STDERR_WRITE
putc(']',stderr);
vfprintf(stderr,format,args);
#endif
va_end(args);
return;
}
ip = command_giver->interactive;
new_string_data[0] = '\0';
/*
this is dangerous since the data may not all fit into new_string_data
but how to tell if it will without trying it first? I suppose one
could rewrite vsprintf to accept a maximum length (like strncpy) --
have fun!
*/
vsprintf(new_string_data, format, args);
va_end(args);
#ifndef NO_SHADOWS
/*
* shadow handling.
*/
if (shadow_catch_message(command_giver,new_string_data)) {
/*
* snoop handling.
*/
#ifdef SNOOP_SHADOWED
if (ip->snoop_by) {
save_command_giver = command_giver;
command_giver = ip->snoop_by->ob;
#ifdef RECEIVE_SNOOP
receive_snoop(new_string_data, command_giver);
#else
add_message("$$ %s",new_string_data);
#endif
command_giver = save_command_giver;
}
#endif
return;
}
#endif /* NO_SHADOWS */
/*
* write message into command_giver->interactive->message_buf.
*/
for (cp = new_string_data; *cp != '\0'; cp++) {
if (ip->message_length == MESSAGE_BUF_SIZE) {
if (!flush_message()) {
fprintf (stderr,"Broken connection during add_message.\n");
return;
}
if (ip->message_length == MESSAGE_BUF_SIZE)
break;
}
if (*cp == '\n') {
if (ip->message_length == (MESSAGE_BUF_SIZE-1)) {
if (!flush_message()) {
fprintf (stderr,"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()) {
fprintf (stderr,"Broken connection during add_message.\n");
return;
}
}
/*
* snoop handling.
*/
if (ip->snoop_by) {
save_command_giver = command_giver;
command_giver = ip->snoop_by->ob;
#ifdef RECEIVE_SNOOP
receive_snoop(new_string_data, command_giver);
#else
add_message("%% %s",new_string_data);
#endif
command_giver = save_command_giver;
}
#ifdef COMM_STAT
add_message_calls++;
#endif /* COMM_STAT */
}
/*
* Flush outgoing message buffer of current interactive object.
*/
int flush_message()
{
struct interactive *ip;
int length, num_bytes;
/*
* if command_giver->interactive is not valid, do nothing.
*/
if(command_giver == 0 ||
(command_giver->flags & O_DESTRUCTED) ||
command_giver->interactive == 0 ||
command_giver->interactive->closing){
fprintf(stderr,"flush_message: invalid command_giver!\n");
return 0;
}
ip = command_giver->interactive;
/*
* write command_giver->interactive->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);
*/
num_bytes = send(ip->fd,ip->message_buf + ip->message_consumer,
length, ip->out_of_band);
if (num_bytes == -1) {
if (errno == EWOULDBLOCK) {
debug(512,("flush_message: write: Operation would block\n"));
return 1;
} else {
fprintf(stderr,"flush_message: write on fd %d\n",ip->fd);
perror("flush_message: write");
ip->net_dead = 1;
return 0;
}
}
ip->message_consumer = (ip->message_consumer + num_bytes) %
MESSAGE_BUF_SIZE;
ip->message_length -= num_bytes;
ip->out_of_band = 0;
#ifdef COMM_STAT
inet_packets++;
inet_volume += num_bytes;
#endif /* COMM_STAT */
}
return 1;
}
#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
/*
* Copy a string, replacing newlines with '\0'. Also add an extra
* space and back space for every newline. This trick will allow
* otherwise empty lines, as multiple newlines would be replaced by
* multiple zeroes only.
*
* Also handle the telnet stuff. So instead of this being a direct
* copy it is a small state thingy.
*
* In fact, it is telnet_neg conglomerated into this. This is mostly
* done so we can sanely remove the telnet sub option negotation stuff
* out of the input stream. Need this for terminal types.
* (Pinkfish change)
*/
static int copy_chars(from, to, n, ip)
unsigned char *from, *to;
int n;
struct interactive *ip;
{
int i;
struct object *save_command_giver;
unsigned char *start = to;
for (i=0;i<n;i++) {
switch (ip->state) {
case TS_DATA :
switch (from[i]) {
case IAC :
ip->state = TS_IAC;
break;
case '\r' :
if (ip->single_char)
*to++ = from[i];
break;
case '\n' :
if (ip->single_char)
*to++ = from[i];
else {
*to++ = ' ';
*to++ = '\b';
*to++ = '\0';
}
break;
default :
*to++ = from[i];
/* single character mode */
/* ack! special case so we don't pick up flow control chars */
break;
}
break;
case TS_IAC :
switch (from[i]) {
case DO :
ip->state = TS_DO;
break;
case DONT :
ip->state = TS_DONT;
break;
case WILL :
ip->state = TS_WILL;
break;
case WONT :
ip->state = TS_WONT;
break;
case BREAK :
/* Send back a break character. */
save_command_giver = command_giver;
command_giver = ip->ob;
add_message("%c", '\34');
add_message("%c%c%c", IAC, WILL, TELOPT_TM);
flush_message();
command_giver = save_command_giver;
break;
case IP :
/* Send back an interupt process character. */
save_command_giver = command_giver;
command_giver = ip->ob;
add_message("%c", '\177');
add_message("%c%c%c", IAC, WILL, TELOPT_TM);
flush_message();
command_giver = save_command_giver;
break;
case AYT :
/* Are you there signal. Yep we are. */
save_command_giver = command_giver;
command_giver = ip->ob;
add_message("\n[Yes]\n");
command_giver = save_command_giver;
break;
case AO :
/* Abort output. Do a telnet sync operation. */
save_command_giver = command_giver;
command_giver = ip->ob;
ip->out_of_band = MSG_OOB;
add_message("%c%c", IAC, DM);
flush_message();
command_giver = save_command_giver;
break;
case SB :
ip->state = TS_SB;
ip->sb_pos = 0;
break;
/* SE counts as going back into data mode */
case SE :
/*
* Ok... need to call a function on the interactive object, passing the
* buffer as a paramater.
*/
save_command_giver = command_giver;
command_giver = ip->ob;
ip->sb_buf[ip->sb_pos] = 0;
push_constant_string(ip->sb_buf);
apply("telnet_suboption", ip->ob, 1);
command_giver = save_command_giver;
ip->state = TS_DATA;
break;
case DM :
default :
ip->state = TS_DATA;
break;
}
break;
case TS_DO :
if (from[i] == TELOPT_TM) {
save_command_giver = command_giver;
command_giver = ip->ob;
add_message("%c%c%c", IAC, WILL, TELOPT_TM);
flush_message();
command_giver = save_command_giver;
}
case TS_DONT :
case TS_WILL :
case TS_WONT :
ip->state = TS_DATA;
break;
case TS_SB :
if ((unsigned char)from[i] == IAC) {
ip->state = TS_IAC;
break;
}
/* Ok, put all the suboption stuff into the buffer on the interactive */
if (ip->sb_pos >= SB_SIZE)
break; /* Ignore stuff outside the range */
if (from[i])
ip->sb_buf[ip->sb_pos++] = from[i];
else
ip->sb_buf[ip->sb_pos++] = 'I'; /* Turn 0's into I's */
break;
}
}
return(to - start);
}
/*
* SIGPIPE handler -- does very little for now.
*/
void sigpipe_handler()
{
fprintf(stderr,"SIGPIPE received.\n");
}
/*
* SIGALRM handler.
*/
void sigalrm_handler()
{
heart_beat_flag = 1;
debug(512,("sigalrm_handler: SIGALRM\n"));
}
INLINE void make_selectmasks()
{
int i;
/*
* generate readmask and writemask for select() call.
*/
FD_ZERO(&readmask);
FD_ZERO(&writemask);
/*
* set new user accept fd in readmask.
*/
FD_SET(new_user_fd,&readmask);
/*
* set user fds in readmask.
*/
for(i=0;i<MAX_USERS;i++){
if(!all_users[i] || all_users[i]->closing || all_users[i]->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]->message_length != 0)
FD_SET(all_users[i]->fd,&writemask);
}
/*
* if f_ip_demon is set, set its fd in readmask. f_ip_demon is hname.
*/
if(addr_server_fd != 0) {
FD_SET(addr_server_fd,&readmask);
}
#ifdef SOCKET_EFUNS
/*
* set fd's for efun sockets.
*/
for(i=0;i<MAX_EFUN_SOCKS;i++){
if(lpc_socks[i].state != CLOSED){
if ((lpc_socks[i].flags & S_WACCEPT) == 0)
FD_SET(lpc_socks[i].fd,&readmask);
if (lpc_socks[i].flags & S_BLOCKED)
FD_SET(lpc_socks[i].fd,&writemask);
}
}
#endif
}
/*
* Process I/O.
*/
INLINE void process_io()
{
int i;
struct object *save_command_giver;
debug(256,("@"));
/*
* check for new user connection.
*/
if(FD_ISSET(new_user_fd,&readmask)){
debug(512,("process_io: NEW_USER\n"));
new_user_handler();
}
/*
* check for data pending on user connections.
*/
for(i=0;i<MAX_USERS;i++){
if(!all_users[i] || all_users[i]->closing || all_users[i]->cmd_in_buf)
continue;
if (all_users[i]->net_dead) {
remove_interactive(all_users[i]->ob);
continue;
}
if(FD_ISSET(all_users[i]->fd,&readmask)){
debug(512,("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)){
save_command_giver = command_giver;
command_giver = all_users[i]->ob;
flush_message();
command_giver = save_command_giver;
}
}
#ifdef SOCKET_EFUNS
/*
* check for data pending on efun socket connections.
*/
for(i=0;i<MAX_EFUN_SOCKS;i++){
if(lpc_socks[i].state != CLOSED)
if(FD_ISSET(lpc_socks[i].fd,&readmask))
socket_read_select_handler(i);
if(lpc_socks[i].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(512,("process_io: IP_DAEMON\n"));
hname_handler();
}
}
}
/*
* 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.
*/
void new_user_handler()
{
int new_socket_fd;
struct sockaddr_in addr;
int length;
int i;
char *full_message;
#ifdef ACCESS_RESTRICTED
void *class;
#endif /* ACCESS_RESTRICTED */
struct object *ob;
struct svalue *ret;
extern struct object *master_ob;
length = sizeof(addr);
debug(512,("new_user_handler: accept on fd %d\n",new_user_fd));
new_socket_fd = accept(new_user_fd,(struct sockaddr *)&addr,(int *)&length);
if(new_socket_fd < 0){
if(errno == EWOULDBLOCK){
debug(512,("new_user_handler: accept: Operation would block\n"));
}
else {
perror("new_user_handler: accept");
}
return;
}
#ifdef ACCESS_RESTRICTED
if(!(class = allow_host_access(new_socket_fd, new_socket_fd))){
fprintf(stderr,"new_user_handler: allow_host_access denied.\n");
return;
}
#else /* !ACCESS_RESTRICTED */
if(allow_host_access(new_socket_fd)){
fprintf(stderr,"new_user_handler: allow_host_access denied.\n");
return;
}
#endif /* ACCESS_RESTRICTED */
for(i=0;i<MAX_USERS;i++){
if(all_users[i] != 0)
continue;
assert_master_ob_loaded();
command_giver = master_ob;
master_ob->interactive =
(struct interactive *)
DXALLOC(sizeof(struct interactive), 20, "new_user_handler");
total_users++;
master_ob->interactive->default_err_message = 0;
master_ob->flags |= O_ONCE_INTERACTIVE;
/*
* initialize new user interactive data structure.
*/
master_ob->interactive->ob = master_ob;
master_ob->interactive->input_to = 0;
master_ob->interactive->closing = 0;
master_ob->interactive->text[0] = '\0';
master_ob->interactive->text_end = 0;
master_ob->interactive->text_start = 0;
master_ob->interactive->cmd_in_buf = 0;
master_ob->interactive->carryover = NULL;
master_ob->interactive->snoop_on = 0;
master_ob->interactive->snoop_by = 0;
master_ob->interactive->noecho = 0;
master_ob->interactive->noesc = 0;
master_ob->interactive->last_time = current_time;
master_ob->interactive->trace_level = 0;
master_ob->interactive->trace_prefix = 0;
master_ob->interactive->ed_buffer = 0;
master_ob->interactive->message_producer = 0;
master_ob->interactive->message_consumer = 0;
master_ob->interactive->message_length = 0;
master_ob->interactive->num_carry = 0;
master_ob->interactive->net_dead = 0;
master_ob->interactive->state = TS_DATA;
master_ob->interactive->out_of_band = 0;
master_ob->interactive->single_char = 0;
all_users[i] = master_ob->interactive;
all_users[i]->fd = new_socket_fd;
set_prompt("> ");
memcpy((char *)&all_users[i]->addr, (char *)&addr, length);
#ifdef ACCESS_RESTRICTED
all_users[i]->access_class = class;
#endif /* ACCESS_RESTRICTED */
num_user++;
debug(512,("New connection from %s.\n",inet_ntoa(addr.sin_addr)));
/*
* The user object has one extra reference.
* It is asserted that the master_ob is loaded.
*/
add_ref(master_ob, "new_user");
ret = apply_master_ob("connect", 0);
if(ret == 0 || ret->type != T_OBJECT){
remove_interactive(master_ob);
fprintf(stderr,"Connection from %s aborted.\n",inet_ntoa(addr.sin_addr));
return;
}
/*
* There was an object returned from connect(). Use this as the
* user object.
*/
ob = ret->u.ob;
if(ob->flags & O_HIDDEN)
num_hidden++;
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->has_write_prompt = 1;
ob->interactive->has_process_input = 1;
master_ob->flags &= ~O_ONCE_INTERACTIVE;
master_ob->interactive = 0;
free_object(master_ob, "reconnect");
add_ref(ob, "new_user");
command_giver = ob;
if(addr_server_fd != 0){
query_addr_name(ob);
}
logon(ob);
debug(512,("new_user_handler: end\n"));
return;
}
full_message = "No user slots available: closing connection...\r\n";
write(new_socket_fd, full_message, strlen(full_message));
if(close(new_socket_fd) == -1){
perror("new_user_handler: close");
}
}
/*
* 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;
static char buf[MAX_TEXT], *tbuf;
struct object *save_current_object = current_object;
struct object *save_command_giver = command_giver;
struct svalue *ret;
if((user_command = get_user_command())){
if(command_giver->flags & O_DESTRUCTED)
return(1);
clear_notify(); /* moved from user_parser() */
update_load_av();
current_object = 0;
current_interactive = command_giver;
debug(512,("process_user_command: command_giver = %s\n",
command_giver->name));
tbuf = user_command;
if ((user_command[0] == '!') && (command_giver->interactive->ed_buffer
|| (command_giver->interactive->input_to
&& !command_giver->interactive->noesc)))
{
if(command_giver->interactive->has_process_input){
push_constant_string(user_command + 1); /* not malloc'ed */
ret = apply("process_input",command_giver,1);
if(ret && (ret->type == T_STRING) && ret->u.string){
strncpy(buf+1,ret->u.string,MAX_TEXT - 2);
tbuf = buf;
} else if (command_giver->interactive) {
command_giver->interactive->has_process_input = 0;
}
}
if (!command_giver || (command_giver->flags & O_DESTRUCTED)) {
return 1;
}
parse_command(tbuf+1,command_giver);
} else if(command_giver->interactive->ed_buffer){
#ifdef ED
ed_cmd(user_command);
#endif /* ED */
} else if (call_function_interactive(command_giver->interactive,
user_command)) {
; /* do nothing */
} else {
/*
* send a copy of user input back to user object to
* provide support for things like command history and
* mud shell programming languages.
*/
if(command_giver->interactive->has_process_input){
push_constant_string(user_command); /* not malloc'ed */
ret = apply("process_input",command_giver,1);
if (ret && (ret->type == T_STRING) && ret->u.string) {
strncpy(buf,ret->u.string,MAX_TEXT - 1);
tbuf = buf;
} else if (command_giver->interactive) {
command_giver->interactive->has_process_input = 0;
}
}
if (!command_giver || command_giver->flags & O_DESTRUCTED) {
return 1;
}
parse_command(tbuf,command_giver);
}
/*
* Print a prompt if user is still here.
*/
if(command_giver->interactive)
print_prompt();
current_object = save_current_object;
command_giver = save_command_giver;
return(1);
}
current_object = save_current_object;
command_giver = save_command_giver;
return(0);
}
#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 (f_ip_demon).
*/
void hname_handler()
{
static char hname_buf[HNAME_BUF_SIZE];
int num_bytes;
int tmp;
char *pp, *q;
long laddr;
if(addr_server_fd == 0) return;
num_bytes = read(addr_server_fd,hname_buf,HNAME_BUF_SIZE);
switch(num_bytes){
case -1:
switch(errno){
case EWOULDBLOCK:
debug(512,("hname_handler: read on fd %d: Operation would block.\n",
addr_server_fd));
break;
default:
fprintf(stderr,"hname_handler: read on fd %d\n",addr_server_fd);
perror("hname_handler: read");
tmp = addr_server_fd;
addr_server_fd = 0;
close(tmp);
return;
break;
}
break;
case 0:
fprintf(stderr,"hname_handler: closing address server connection.\n");
tmp = addr_server_fd;
addr_server_fd = 0;
close(tmp);
return;
break;
default:
hname_buf[num_bytes] = '\0';
debug(512,("hname_handler: address server replies: %s",hname_buf));
if (hname_buf[0] >= '0' && hname_buf[0] <= '9') {
laddr = inet_addr(hname_buf);
if(laddr != -1){
pp = strchr(hname_buf,' ');
if(pp){
q = strchr(hname_buf,'\n');
*pp = 0;
pp++;
if(q) {
*q = 0;
if (strcmp(pp, "0"))
add_ip_entry(laddr,pp);
got_addr_number(pp, hname_buf); /* Recognises this as failure. */
}
}
}
} else {
char *r;
/* This means it was a name lookup... */
pp = strchr(hname_buf, ' ');
if (pp) {
*pp = 0;
pp++;
r = strchr(pp, '\n');
if (r)
*r = 0;
got_addr_number(pp, hname_buf);
}
}
break;
}
}
/*
* Read pending data for a user into user->interactive->text.
* This also does telnet negotiation.
*/
void get_user_data(ip)
struct interactive *ip;
{
struct object *save_command_giver;
static char buf[MAX_TEXT];
int text_space;
int num_bytes;
/*
* this /3 is here because of the trick copy_chars() uses to allow
* empty commands. it needs to be fixed right. later.
*/
text_space = (MAX_TEXT - ip->text_end - 1) / 3;
/*
* read user data.
*/
debug(512,("get_user_data: read on fd %d\n",ip->fd));
num_bytes = read(ip->fd,buf,text_space);
switch(num_bytes){
case 0:
if(ip->closing)
fprintf(stderr,"get_user_data: tried to read from closing fd.\n");
remove_interactive(ip->ob);
return;
break;
case -1:
if(errno == EWOULDBLOCK){
debug(512,("get_user_data: read on fd %d: Operation would block.\n",
ip->fd));
}
else {
fprintf(stderr,"get_user_data: read on fd %d\n",ip->fd);
perror("get_user_data: read");
remove_interactive(ip->ob);
return;
}
break;
default:
buf[num_bytes] = '\0';
/*
* replace newlines with nulls and catenate to buffer.
* Also do all the useful telnet negotation at this point too.
* Rip out the sub option stuff and send back anything non useful
* we feel we have to.
*/
ip->text_end += copy_chars(buf,ip->text + ip->text_end,num_bytes,ip);
/*
* now, text->end is just after the last char read. If last
* char was a nl, char *before* text_end will be null.
*/
ip->text[ip->text_end] = '\0';
/*
* handle snooping - snooper does not see type-ahead. seems like that
* would be very inefficient, for little functional gain.
*/
if(ip->snoop_by && !ip->noecho){
save_command_giver = command_giver;
command_giver = ip->snoop_by->ob;
#ifdef RECEIVE_SNOOP
receive_snoop(buf, command_giver);
#else
add_message("%%%s",buf);
#endif
command_giver = save_command_giver;
}
/*
* set flag if new data completes command.
*/
if(cmd_in_buf(ip)) ip->cmd_in_buf = 1;
break;
}
}
/*
* Return the first cmd of the next user in sequence that has a complete cmd
* in their buffer.
* CmdsGiven is used to allow users in ED to send more cmds (if they have
* them queued up) than users not in ED.
* This should also return a value if there is something in the
* buffer and we are supposed to be in single character mode.
*/
#define StartCmdGiver (MAX_USERS-1)
#define IncCmdGiver NextCmdGiver = (NextCmdGiver == 0? StartCmdGiver: \
NextCmdGiver - 1)
static int NextCmdGiver = StartCmdGiver;
char *get_user_command()
{
int i;
struct interactive *ip;
char *user_command = NULL;
static char buf[MAX_TEXT];
/*
* find and return a user command.
*/
for(i=0;i<MAX_USERS;i++){
ip = all_users[NextCmdGiver];
if(ip && ip->cmd_in_buf){
user_command = first_cmd_in_buf(ip);
break;
}
IncCmdGiver;
}
/*
* no cmds found; return(NULL).
*/
if(!ip || !user_command) return((char *)NULL);
/*
* we have a user cmd -- return it.
* If user has only one partially completed cmd left after this,
* move it to the start of his buffer; new stuff will be appended.
*/
debug(512,("get_user_command: user_command = (%s)\n",user_command));
command_giver = ip->ob;
/*
* telnet option parsing and negotiation.
*/
telnet_neg(buf,user_command);
/*
* move input buffer pointers to next command.
*/
next_cmd_in_buf(ip);
if(!cmd_in_buf(ip)) ip->cmd_in_buf = 0;
IncCmdGiver;
if(ip->noecho){
/*
* Must not enable echo before the user input is received.
*/
add_message("%c%c%c",IAC,WONT,TELOPT_ECHO);
ip->noecho = 0;
}
ip->last_time = current_time;
return(buf);
}
/*
* 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 null between
* text_start and text_end. Zero length commands are discarded (as occur
* between <cr> and <lf>). Update text_start if we have to skip leading
* nulls.
* This should return true when in single char mode and there is
* Anything at all in the buffer.
*/
char *first_cmd_in_buf(ip)
struct interactive *ip;
{
char *p, *q;
p = ip->text + ip->text_start;
/*
* skip null input.
*/
while((p < (ip->text + ip->text_end)) && !*p)
p++;
ip->text_start = p - ip->text;
if(ip->text_start >= ip->text_end){
ip->text_start = ip->text_end = 0;
ip->text[0] = '\0';
return((char *)NULL);
}
/* If we got here, must have something in the array */
if (ip->single_char) {
/* We need to return true here... */
return (ip->text + ip->text_start);
}
/*
* find end of cmd.
*/
while((p < (ip->text + ip->text_end)) && *p)
p++;
/*
* null terminated; was command.
*/
if(p < ip->text + ip->text_end)
return(ip->text + ip->text_start);
/*
* have a partial command at end of buffer; move it to start, return null.
* if it can't move down, truncate it and return it as cmd.
*/
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 - 2){
ip->text[ip->text_end-2] = '\0'; /* nulls to truncate */
ip->text[ip->text_end-1] = '\0'; /* nulls to truncate */
ip->text_end--;
return(ip->text);
}
/*
* buffer not full and no newline - no cmd.
*/
return((char *)NULL);
}
/*
* return(1) if there is a complete command in ip->text, otherwise return(0).
*/
int cmd_in_buf(ip)
struct interactive *ip;
{
char *p;
p = ip->text + ip->text_start;
/*
* skip null input.
*/
while((p < (ip->text + ip->text_end)) && !*p)
p++;
if((p - ip->text) >= ip->text_end){
return(0);
}
/* If we get here, must have something in the buffer */
if (ip->single_char) {
return (1);
}
/*
* find end of cmd.
*/
while((p < (ip->text + ip->text_end)) && *p)
p++;
/*
* null terminated; was command.
*/
if(p < ip->text + ip->text_end)
return(1);
/*
* no newline - no cmd.
*/
return(0);
}
/*
* move pointers to next cmd, or clear buf.
*/
void next_cmd_in_buf(ip)
struct interactive *ip;
{
char *p = ip->text + ip->text_start;
while(*p && p < ip->text + ip->text_end)
p++;
/*
* skip past any nulls at the end.
*/
while(!*p && p < ip->text + ip->text_end)
p++;
if(p < ip->text + ip->text_end)
ip->text_start = p - ip->text;
else {
ip->text_start = ip->text_end = 0;
ip->text[0] = '\0';
}
}
/*
* Remove an interactive user immediately.
*/
void remove_interactive(ob)
struct object *ob;
{
struct object *save_command_giver = command_giver;
int i;
if (!ob->interactive) {
return;
}
for(i=0;i<MAX_USERS;i++){
if(all_users[i] != ob->interactive)
continue;
if(ob->interactive->closing){
fprintf(stderr,"Double call to remove_interactive()\n");
return;
}
ob->interactive->closing = 1;
/*
* auto-notification of net death
*/
safe_apply("net_dead", ob, 0);
if(ob->interactive->snoop_by){
ob->interactive->snoop_by->snoop_on = 0;
ob->interactive->snoop_by = 0;
}
if(ob->interactive->snoop_on){
ob->interactive->snoop_on->snoop_by = 0;
ob->interactive->snoop_on = 0;
}
command_giver = ob;
debug(512,("Closing connection from %s.\n",
inet_ntoa(ob->interactive->addr.sin_addr)));
if(ob->interactive->ed_buffer){
#ifdef ED
save_ed_buffer();
#endif
}
#if !defined(ultrix)
if(shutdown(ob->interactive->fd,0) == -1){
perror("remove_interactive: shutdown");
}
#else /* ultrix */
shutdown(ob->interactive->fd,0);
#endif /* !defined(ultrix) */
debug(512,("remove_interactive: closing fd %d\n",ob->interactive->fd));
if(close(ob->interactive->fd) == -1){
perror("remove_interactive: close");
}
#ifdef ACCESS_RESTRICTED
release_host_access(ob->interactive->access_class);
#endif /* ACCESS_RESTRICTED */
if(ob->flags & O_HIDDEN)
num_hidden--;
num_user--;
if(ob->interactive->input_to){
free_object(ob->interactive->input_to->ob, "remove_interactive");
free_sentence(ob->interactive->input_to);
if(ob->interactive->num_carry > 0)
free_some_svalues(ob->interactive->carryover,
ob->interactive->num_carry);
ob->interactive->carryover = NULL;
ob->interactive->num_carry = 0;
ob->interactive->input_to = 0;
}
FREE((char *)ob->interactive);
total_users--;
ob->interactive = 0;
all_users[i] = 0;
free_object(ob, "remove_interactive");
command_giver = save_command_giver;
return;
}
fprintf(stderr,"remove_interactive: could not find and remove user %s\n",
ob->name);
abort();
}
#ifndef ACCESS_RESTRICTED
int allow_host_access(new_socket)
int new_socket;
{
struct sockaddr_in apa;
int len = sizeof apa;
char *ipname, *xalloc();
static int read_access_list = 0;
static struct access_list {
int addr_len;
char * addr, *name, *comment;
struct access_list * next;
} *access_list;
register struct access_list * ap;
if(!read_access_list) {
FILE * f = fopen("ACCESS.DENY", "r");
char buf[1024], ipn[50], hname[100], comment[1024], *p1, *p2;
struct access_list * na;
struct hostent * hent;
read_access_list = 1;
if(f) {
while(fgets(buf, sizeof buf - 1, f)) {
if(*buf != '#') {
ipn[0] = hname[0] = comment[0] = 0;
if((p1 = strchr(buf, ':'))) *p1 = 0;
if(buf[0] && buf[0] != '\n')
strncpy(ipn, buf, sizeof ipn - 1);
if((p2 = p1) && *++p2) {
if((p1 = strchr(p2, ':'))) *p1 = 0;
if(p2[0] && p2[0] != '\n')
strcpy(hname, p2);
if(p1 && p1[1] && p1[1] != '\n')
strcpy(comment, p1+1);
}
if(!(na = (struct access_list *)
DXALLOC(sizeof na[0], 21, "allow_host_access: na"))) {
fatal("Out of mem.\n");
}
na->addr = na->name = na->comment = 0;
na->next = 0;
if(*ipn && (!(na->addr = DXALLOC(strlen(ipn) + 1, 22,
"allow_host_access: na->addr")) ||
!strcpy(na->addr, ipn)))
fatal("Out of mem.\n");
if(*hname && (!(na->name =
DXALLOC(strlen(hname) + 1, 23, "allow_host_access: na->name"))
|| !strcpy(na->name, hname)))
fatal("Out of mem.\n");
if(*comment
&& (!(na->comment=
DXALLOC(strlen(comment)+1, 24, "allow_host_access: na->comment"))
|| !strcpy(na->comment, comment)))
fatal("Out of mem.\n");
if((!(int)*ipn) && ((!*hname) || (!(hent = gethostbyname(hname))) ||
(!(na->addr = DXALLOC(hent->h_length+1, 25,
"allow_host_access: na->addr")))||
!strcpy(na->addr,
inet_ntoa(*(struct in_addr *)hent->h_addr)))) {
if(na->name) FREE(na->name);
if(na->comment) FREE(na->comment);
FREE((char *)na);
continue;
}
if(!(na->addr_len = strlen(na->addr)))
continue;
/* printf("disabling: %s:%s:%s\n", na->addr,
na->name?na->name:"no name",
na->comment?na->comment:"no comment"); */
na->next = access_list;
access_list = na;
}
}
fclose(f);
}
}
if(!access_list)
return(0);
if(getpeername(new_socket, (struct sockaddr *)&apa, &len) == -1) {
if(close(new_socket) == -1){
perror("allow_host_access: close");
}
perror("allow_host_access: getpeername");
return(-1);
}
ipname = inet_ntoa(apa.sin_addr);
for(ap = access_list; ap; ap = ap->next)
if(!strncmp(ipname, ap->addr, ap->addr_len)){
if(ap->comment) (void) write(new_socket, ap->comment,
strlen(ap->comment));
printf("Stopping: %s:%s\n", ap->addr, ap->name?ap->name:"no name");
if(close(new_socket) == -1){
perror("allow_host_access: close");
}
return(-1);
}
return(0);
}
#endif /* not ACCESS_RESTRICTED */
int call_function_interactive(i, str)
struct interactive *i;
char *str;
{
char *function;
struct object *ob;
struct svalue *args;
int num_arg, tindex;
i->noesc = 0;
if(!i->input_to)
return(0);
/*
* Special feature: input_to() has been called to setup
* a call to a function.
*/
if(i->input_to->ob->flags & O_DESTRUCTED){
/* Sorry, the object has selfdestructed ! */
free_object(i->input_to->ob, "call_function_interactive");
free_sentence(i->input_to);
i->input_to = 0;
if(i->num_carry)
free_some_svalues(i->carryover, i->num_carry);
i->carryover = NULL;
i->num_carry = 0;
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().
*/
free_object(i->input_to->ob, "call_function_interactive");
function = string_copy(command_giver->interactive->input_to->function);
ob = i->input_to->ob;
free_sentence(i->input_to);
/* 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){
if((args=(struct svalue *)
DXALLOC(num_arg * sizeof(struct svalue),26,"comm.c: input_to")) == NULL)
fatal("Not enough memory for input_to.");
copy_some_svalues(args, i->carryover, i->num_carry);
free_some_svalues(i->carryover, i->num_carry);
i->num_carry = 0;
i->carryover = NULL;
}
else
args = NULL;
i->input_to = 0;
if (i->single_char) {
/*
* clear single character mode
*/
i->single_char = 0;
add_message("%c%c%c", IAC, WONT, TELOPT_SGA);
}
/*
* 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.
*/
push_constant_string(str);
for(tindex = 0; tindex < num_arg; tindex++)
push_svalue(&args[tindex]);
/*
* Now we set current_object to this object, so that input_to will
* work for static functions.
*/
current_object = ob;
(void)apply(function, ob, num_arg + 1);
FREE(function);
free_some_svalues(args, num_arg);
return(1);
}
int set_call(ob, sent, flags, single_char)
struct object *ob;
struct sentence *sent;
int flags;
int single_char;
{
struct object *save_command_giver = command_giver;
if(ob == 0 || sent == 0)
return(0);
if(ob->interactive == 0 || ob->interactive->input_to)
return(0);
ob->interactive->input_to = sent;
ob->interactive->noecho = ((flags & I_NOECHO) != 0);
ob->interactive->noesc = ((flags & I_NOESC) != 0);
ob->interactive->single_char = single_char;
command_giver = ob;
if (flags & I_NOECHO)
add_message("%c%c%c", IAC, WILL, TELOPT_ECHO);
if (single_char)
add_message("%c%c%c", IAC, WILL, TELOPT_SGA);
command_giver = save_command_giver;
return(1);
}
void
set_prompt(str)
char *str;
{
if (command_giver && command_giver->interactive) {
command_giver->interactive->prompt = str;
}
}
/*
* Print the prompt, but only if input_to not is disabled.
*/
void print_prompt()
{
if(command_giver == 0)
fatal("command_giver == 0.\n");
if(command_giver->interactive->input_to == 0){
/* give user object a chance to write its own prompt */
if(!command_giver->interactive->has_write_prompt)
add_message(command_giver->interactive->prompt);
else if(command_giver->interactive&&command_giver->interactive->ed_buffer)
add_message(command_giver->interactive->prompt);
else if(!(command_giver->flags & O_DESTRUCTED) &&
!apply("write_prompt",command_giver,0)) {
if (command_giver->interactive) {
command_giver->interactive->has_write_prompt = 0;
add_message(command_giver->interactive->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.
*/
int new_set_snoop(me, you)
struct object *me, *you;
{
struct interactive *on = 0, *by = 0, *tmp;
int i;
/*
* Stop if people managed to quit before we got this far.
*/
if(me->flags & O_DESTRUCTED)
return(0);
if(you && (you->flags & O_DESTRUCTED))
return(0);
/*
* Find the snooper && snoopee.
*/
for(i = 0 ; i < MAX_USERS && (on == 0 || by == 0); i++){
if(all_users[i] == 0)
continue;
if(all_users[i]->ob == me)
by = all_users[i];
else if(all_users[i]->ob == you)
on = all_users[i];
}
/*
* Stop snoop.
*/
if(you == 0){
if(by == 0)
error("Could not find snooper to stop snoop on.\n");
if(by->snoop_on == 0)
return(1);
by->snoop_on->snoop_by = 0;
by->snoop_on = 0;
return(1);
}
/*
* Strange event, but possible, so test for it.
*/
if(on == 0 || by == 0)
return(0);
/*
* Protect against snooping loops.
*/
for(tmp = on; tmp; tmp = tmp->snoop_on){
if(tmp == by)
return(0);
}
/*
* Terminate previous snoop, if any.
*/
if(by->snoop_on){
by->snoop_on->snoop_by = 0;
by->snoop_on = 0;
}
if(on->snoop_by){
on->snoop_by->snoop_on = 0;
on->snoop_by = 0;
}
on->snoop_by = by;
by->snoop_on = on;
return(1);
}
/*
* Bit of a misnomer now. But I can't be bothered changeing the
* name. This will handle backspace resolution amongst other things,
* (Pinkfish change)
*/
void telnet_neg(to, from)
char *to, *from;
{
int ch;
char *first = to;
while(1){
ch = (*from++ & 0xff);
switch(ch){
case '\b': /* Backspace */
case 0x7f: /* Delete */
if(to <= first)
continue;
to -= 1;
continue;
default:
if(ch & 0x80){
continue;
}
*to++ = ch;
if(ch == 0)
return;
continue;
} /* switch() */
} /* while() */
} /* telnet_neg() */
void query_addr_name(ob)
struct object *ob;
{
static char buf[80];
int msgtype = NAMEBYIP;
memcpy(buf,(char *)&msgtype,sizeof(msgtype));
sprintf(&buf[sizeof(int)],"%s",query_ip_number(ob));
debug(512,("query_addr_name: sent address server %s\n",&buf[sizeof(int)]));
if(write(addr_server_fd,buf,sizeof(int) + strlen(&buf[sizeof(int)]) + 1)
== -1){
switch(errno){
case EBADF:
fprintf(stderr,"Address server has closed connection.\n");
addr_server_fd = 0;
break;
default:
perror("query_addr_name: write");
break;
}
}
}
#define IPSIZE 200
static struct ipnumberentry {
char *name,
*call_back;
struct object *ob_to_call;
} ipnumbertable[IPSIZE];
/*
* Does a call back on the current_object with the function call_back.
*/
int query_addr_number(name, call_back)
char *name, *call_back;
{
static char buf[80];
int msgtype = IPBYNAME;
if (name[0] >= '0' && name[0] <= '9')
msgtype = NAMEBYIP;
memcpy(buf,(char *)&msgtype,sizeof(msgtype));
if (!addr_server_fd || strlen(name) > 80-sizeof(msgtype)) {
push_constant_string(name);
push_null();
apply(call_back, current_object, 2);
return 0;
}
sprintf(&buf[sizeof(int)],"%s",name);
debug(512,("query_addr_number: sent address server %s\n",&buf[sizeof(int)]));
if(write(addr_server_fd,buf,sizeof(int) + strlen(&buf[sizeof(int)]) + 1)
== -1){
switch(errno){
case EBADF:
fprintf(stderr,"Address server has closed connection.\n");
addr_server_fd = 0;
break;
default:
perror("query_addr_name: write");
break;
}
push_constant_string(name);
push_null();
apply(call_back, current_object, 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... */
push_constant_string(name);
push_null();
apply(call_back, current_object, 2);
return 0;
}
/* Create our entry... */
ipnumbertable[i].name = make_shared_string(name);
ipnumbertable[i].call_back = make_shared_string(call_back);
ipnumbertable[i].ob_to_call = current_object;
add_ref(current_object, "query_addr_number: ");
return i+1;
}
} /* query_addr_number() */
void got_addr_number(number, name)
char *number, *name;
{
int i;
while (1) {
/* 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_string(ipnumbertable[i].call_back);
free_string(ipnumbertable[i].name);
free_object(ipnumbertable[i].ob_to_call, "got_addr_number: ");
ipnumbertable[i].name = NULL;
}
for (i=0;i < IPSIZE && (!ipnumbertable[i].name
|| (ipnumbertable[i].name && strcmp(name, ipnumbertable[i].name)));i++);
if (i >= IPSIZE) {
/* Hmm, not in the table. Interesting. */
return ;
}
/* Got it, do the call back... */
if (!(ipnumbertable[i].ob_to_call->flags&O_DESTRUCTED)) {
char *theName, *theNumber;
theName = ipnumbertable[i].name;
theNumber = number;
if (isdigit(theName[0])) {
char *tmp;
tmp = theName; theName = theNumber; theNumber = tmp;
}
if (strcmp(theName, "0")) {
push_string(theName, STRING_SHARED);
} else {
push_null();
}
if (strcmp(number, "0")) {
push_string(theNumber, STRING_SHARED);
} else {
push_null();
}
push_number(i+1);
safe_apply(ipnumbertable[i].call_back, ipnumbertable[i].ob_to_call, 3);
}
free_string(ipnumbertable[i].call_back);
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
static struct ipentry {
long addr;
char *name;
} iptable[IPSIZE];
static int ipcur;
char *query_ip_name(ob)
struct object *ob;
{
int i;
if(ob == 0)
ob = command_giver;
if(!ob || ob->interactive == 0)
return((char *)NULL);
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));
}
static void add_ip_entry(addr, name)
long addr;
char *name;
{
int i;
for(i = 0; i < IPSIZE; i++){
if(iptable[i].addr == 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;
}
char *query_ip_number(ob)
struct object *ob;
{
if(ob == 0)
ob = command_giver;
if(!ob || ob->interactive == 0)
return((char *)NULL);
return(inet_ntoa(ob->interactive->addr.sin_addr));
}
#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(ad)
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[20];
gethostname(name,sizeof(name));
name[sizeof(name) - 1] = '\0'; /* Just to make sure */
return(name);
}
struct object *query_snoop(ob)
struct object *ob;
{
if (!ob->interactive || (ob->interactive->snoop_by == 0))
return(0);
return(ob->interactive->snoop_by->ob);
}
struct object *query_snooping(ob)
struct object *ob;
{
if (!ob->interactive || (ob->interactive->snoop_on == 0))
return(0);
return(ob->interactive->snoop_on->ob);
}
int query_idle(ob)
struct object *ob;
{
if(!ob->interactive)
error("query_idle() of non-interactive object.\n");
return(current_time - ob->interactive->last_time);
}
void notify_no_command()
{
char *p,*m;
if(!command_giver || !command_giver->interactive)
return;
p = command_giver->interactive->default_err_message;
if(p){
m = process_string(p); /* We want 'value by function call' /JnA */
#ifndef NO_SHADOWS
if(!shadow_catch_message(command_giver, m))
#endif /* NO_SHADOWS */
add_message(m);
if(m != p)
FREE(m);
free_string(p);
command_giver->interactive->default_err_message = 0;
}
else {
add_message("%s\n", default_fail_message);
}
}
void clear_notify()
{
if(!command_giver->interactive)
return;
if(command_giver->interactive->default_err_message){
free_string(command_giver->interactive->default_err_message);
command_giver->interactive->default_err_message = 0;
}
}
void set_notify_fail_message(str)
char *str;
{
if(!command_giver || !command_giver->interactive)
return;
clear_notify();
if(command_giver->interactive->default_err_message)
free_string(command_giver->interactive->default_err_message);
command_giver->interactive->default_err_message = make_shared_string(str);
}
int replace_interactive(ob, obfrom)
struct object *ob;
struct object *obfrom;
{
/* fprintf(stderr,"DEBUG: %s,%s\n",ob->name,obfrom->name); */
if (ob->interactive) {
error("Bad argument1 to exec()\n");
}
if (!obfrom->interactive) {
error("Bad argument2 to exec()\n");
}
if ((ob->flags & O_HIDDEN) != (obfrom->flags & O_HIDDEN)) {
if (ob->flags & O_HIDDEN) {
num_hidden++;
} else {
num_hidden--;
}
}
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->has_write_prompt = 1;
ob->interactive->has_process_input = 1;
obfrom->interactive = 0;
ob->interactive->ob = ob;
ob->flags |= O_ONCE_INTERACTIVE;
obfrom->flags &= ~O_ONCE_INTERACTIVE;
add_ref(ob, "exec");
free_object(obfrom, "exec");
if (obfrom == command_giver) {
command_giver = ob;
}
return(1);
}
#ifdef DEBUG
/*
* This is used for debugging reference counts.
*/
void update_ref_counts_for_users()
{
int i;
for(i=0; i<MAX_USERS; i++){
if(all_users[i] == 0)
continue;
all_users[i]->ob->extra_ref++;
if(all_users[i]->input_to)
all_users[i]->input_to->ob->extra_ref++;
}
}
#endif /* DEBUG */