/* * comm.c -- communications functions and more. * Dwayne Fontenot (Jacques@TMI) * Windows 95 support by George Reese (Descartes of Borg) */ #include "std.h" #include "network_incl.h" #include "lpc_incl.h" #include "applies.h" #include "main.h" #include "comm.h" #include "socket_efuns.h" #include "backend.h" #include "socket_ctrl.h" #include "eoperators.h" #include "debug.h" #include "ed.h" #include "file.h" #define TELOPTS int total_users = 0; /* * local function prototypes. */ static int flush_message PROT((interactive_t *)); static int copy_chars PROT((unsigned char *, unsigned char *, int, interactive_t *)); #ifdef SIGNAL_FUNC_TAKES_INT static void sigpipe_handler PROT((int)); #else static void sigpipe_handler PROT((void)); #endif static void hname_handler PROT((void)); static void get_user_data PROT((interactive_t *)); static char *get_user_command PROT((void)); static char *first_cmd_in_buf PROT((interactive_t *)); static int cmd_in_buf PROT((interactive_t *)); static void next_cmd_in_buf PROT((interactive_t *)); static int call_function_interactive PROT((interactive_t *, char *)); static void print_prompt PROT((void)); static void telnet_neg PROT((char *, char *)); static void query_addr_name PROT((object_t *)); static void got_addr_number PROT((char *, char *)); static void add_ip_entry PROT((long, char *)); #ifndef NO_ADD_ACTION static void clear_notify PROT((void)); #endif static void new_user_handler PROT((int)); static void receive_snoop PROT((char *, object_t * ob)); /* * 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. */ int add_message_calls = 0; int inet_packets = 0; int inet_volume = 0; interactive_t **all_users = 0; int max_users = 0; /* * private local variables. */ static int addr_server_fd = -1; static void receive_snoop P2(char *, buf, object_t *, snooper) { /* command giver no longer set to snooper */ #ifdef RECEIVE_SNOOP push_constant_string(buf); apply(APPLY_RECEIVE_SNOOP, snooper, 1, ORIGIN_DRIVER); #else /* snoop output is now % in all cases */ add_message(snooper, "%"); add_message(snooper, buf); #endif } /* * Initialize new user connection socket. */ void init_user_conn() { struct sockaddr_in sin; int sin_len; int optval; int i; #ifdef WINSOCK WSADATA WSAData; #define CLEANUP WSACleaup() WSAStartup(MAKEWORD(1, 1), &WDSData); #else #define CLEANUP #endif for (i=0; i < 5; i++) { if (!external_port[i].port) continue; /* * create socket of proper type. */ if ((external_port[i].fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 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) { debug_perror("init_user_conn: setsockopt", 0); CLEANUP; 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) external_port[i].port); /* * bind name to socket. */ if (bind(external_port[i].fd, (struct sockaddr *) & sin, sizeof(sin)) == -1) { debug_perror("init_user_conn: bind", 0); CLEANUP; exit(3); } /* * get socket name. */ sin_len = sizeof(sin); if (getsockname(external_port[i].fd, (struct sockaddr *) & sin, &sin_len) == -1) { debug_perror("init_user_conn: getsockname", 0); CLEANUP; exit(4); } /* * set socket non-blocking, */ if (set_socket_nonblocking(external_port[i].fd, 1) == -1) { debug_perror("init_user_conn: set_socket_nonblocking 1", 0); CLEANUP; exit(8); } /* * listen on socket for connections. */ if (listen(external_port[i].fd, SOMAXCONN) == -1) { debug_perror("init_user_conn: listen", 0); CLEANUP; exit(10); } } /* * register signal handler for SIGPIPE. */ #if !defined(LATTICE) && defined(SIGPIPE) if (signal(SIGPIPE, sigpipe_handler) == SIGNAL_ERROR) { debug_perror("init_user_conn: signal SIGPIPE",0); exit(5); } #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) { debug_perror("ipc_remove: close", 0); } } debug_message("closed external ports\n"); } void init_addr_server P2(char *, hostname, int, addr_server_port) { struct sockaddr_in server; struct hostent *hp; int server_fd; int optval; long addr; if (!hostname) return; /* * get network host data for hostname. */ if (hostname[0] >= '0' && hostname[0] <= '9' && (addr = inet_addr(&hostname[0])) != -1) { hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); } else { hp = gethostbyname(hostname); } if (hp == NULL) { debug_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); if (server_fd < 0) { /* problem opening socket */ debug_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) { debug_perror("init_addr_server: setsockopt", 0); return; } /* * connect socket to server address. */ if (connect(server_fd, (struct sockaddr *) & server, sizeof(server)) == -1) { #ifdef WINSOCK if (errno == WSAECONNREFUSED) #else if (errno == ECONNREFUSED) #endif debug_message("Connection to address server (%s %d) refused.\n", hostname, addr_server_port); else debug_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) { debug_perror("init_addr_server: set_socket_nonblocking 1", 0); return; } } /* * Send a message to an interactive object. If that object is shadowed, * special handling is done. */ void add_message P2(object_t *, who, char *, data) { interactive_t *ip; char *cp; /* * 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); fprintf(stderr, data); #endif #ifdef LATTICE fflush(stderr); #endif return; } ip = who->interactive; #ifndef NO_SHADOWS /* * shadow handling. */ if (shadow_catch_message(who, data)) { /* * snoop handling. */ #ifdef SNOOP_SHADOWED if (ip->snoop_by) receive_snoop(data, ip->snoop_by->ob); #endif return; } #endif /* NO_SHADOWS */ /* * write message into ip->message_buf. */ for (cp = data; *cp != '\0'; cp++) { if (ip->message_length == MESSAGE_BUF_SIZE) { if (!flush_message(ip)) { debug_message("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(ip)) { debug_message("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_message("Broken connection during add_message.\n"); return; } } /* * snoop handling. */ if (ip->snoop_by) receive_snoop(data, ip->snoop_by->ob); add_message_calls++; } /* add_message() */ void add_vmessage P2V(object_t *, who, char *, format) { interactive_t *ip; char *cp, new_string_data[LARGEST_PRINTABLE_STRING]; va_list args; V_DCL(char *format); V_DCL(object_t *who); 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); #ifdef LATTICE fflush(stderr); #endif return; } ip = who->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(who, new_string_data)) { /* * snoop handling. */ #ifdef SNOOP_SHADOWED if (ip->snoop_by) receive_snoop(new_string_data, ip->snoop_by->ob); #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_message("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(ip)) { debug_message("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_message("Broken connection during add_message.\n"); return; } } /* * snoop handling. */ if (ip->snoop_by) receive_snoop(new_string_data, ip->snoop_by->ob); add_message_calls++; } /* add_message() */ /* * Flush outgoing message buffer of current interactive object. */ static int flush_message P1(interactive_t *, ip) { int length, num_bytes; /* * if ip is not valid, do nothing. */ if (!ip || (ip->iflags & CLOSING)) { debug_message("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); */ num_bytes = send(ip->fd, ip->message_buf + ip->message_consumer, length, ip->out_of_band); if (num_bytes == -1) { #ifdef EWOULDBLOCK if (errno == EWOULDBLOCK) { debug(512, ("flush_message: write: Operation would block\n")); return 1; #else # ifdef WINSOCK if (errno == WSAEWOULDBLOCK) { debug(512, ("flush_message: write: Operation would block\n")); return 1; # else if (0) { ; # endif #endif #ifdef linux } else if (errno == EINTR) { debug(512, ("flush_message: write: Interrupted system call")); return 1; #endif } else { debug_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; } return 1; } /* flush_message() */ #define TS_DATA 0 #define TS_IAC 1 #define TS_WILL 2 #define TS_WONT 3 #define TS_DO 4 #define TS_DONT 5 #define TS_SB 6 #define TS_SB_IAC 7 /* * 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) */ /* the codes we send ... */ #define SCHAR SIGNED char static char telnet_break_response[] = { 28, (SCHAR)IAC, (SCHAR)WILL, TELOPT_TM, 0 }; static char telnet_interrupt_response[] = { 127, (SCHAR)IAC, (SCHAR)WILL, TELOPT_TM, 0 }; static char telnet_abort_response[] = { (SCHAR)IAC, (SCHAR)DM, 0 }; static char telnet_do_tm_response[] = { (SCHAR)IAC, (SCHAR)WILL, TELOPT_TM, 0 }; static char telnet_do_naws[] = { (SCHAR)IAC, (SCHAR)DO, TELOPT_NAWS, 0 }; static char telnet_do_ttype[] = { (SCHAR)IAC, (SCHAR)DO, TELOPT_TTYPE, 0 }; static char telnet_term_query[] = { (SCHAR)IAC, (SCHAR)SB, TELOPT_TTYPE, TELQUAL_SEND, (SCHAR)IAC, (SCHAR)SE, 0 }; static char telnet_no_echo[] = { (SCHAR)IAC, (SCHAR)WONT, TELOPT_ECHO, 0 }; static char telnet_no_single[] = { (SCHAR)IAC, (SCHAR)WONT, TELOPT_SGA, 0 }; static char telnet_yes_echo[] = { (SCHAR)IAC, (SCHAR)WILL, TELOPT_ECHO, 0 }; static char telnet_yes_single[] = { (SCHAR)IAC, (SCHAR)WILL, TELOPT_SGA, 0 }; static char telnet_ga[] = { (SCHAR)IAC, (SCHAR)GA, 0 }; static int copy_chars P4(unsigned char *, from, unsigned char *, to, int, n, interactive_t *, ip) { int i; 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->iflags & SINGLE_CHAR) *to++ = from[i]; break; case '\n': if (ip->iflags & SINGLE_CHAR) *to++ = from[i]; else { *to++ = ' '; *to++ = '\b'; *to++ = '\0'; } break; default: *to++ = from[i]; break; } break; case TS_SB_IAC: if (from[i] == IAC) { /* IAC IAC is a quoted IAC char */ ip->sb_buf[ip->sb_pos++] = IAC; ip->state = TS_SB; break; } /* old MudOS treated IAC during IAC SB ... IAC SE as return to data mode. That's probably wrong, but ... fallthrough */ case TS_IAC: switch (from[i]) { case IAC: /* IAC IAC is a quoted IAC char */ *to++ = IAC; ip->state = TS_DATA; break; 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. */ add_message(ip->ob, telnet_break_response); flush_message(ip); break; case IP: /* Send back an interupt process character. */ add_message(ip->ob, telnet_interrupt_response); break; case AYT: /* Are you there signal. Yep we are. */ add_message(ip->ob, "\n[Yes]\n"); break; case AO: /* Abort output. Do a telnet sync operation. */ ip->out_of_band = MSG_OOB; add_message(ip->ob, telnet_abort_response); flush_message(ip); 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. */ ip->sb_buf[ip->sb_pos] = 0; if (ip->sb_buf[0]==TELOPT_TTYPE && ip->sb_buf[1]=='I') { /* TELOPT_TTYPE TELQUAL_IS means it's telling us it's terminal type */ push_constant_string(ip->sb_buf + 2); apply(APPLY_TERMINAL_TYPE, ip->ob, 1, ORIGIN_DRIVER); ip->iflags |= USING_TELNET; } else if (ip->sb_buf[0]==TELOPT_NAWS) { int w, h, i, c[4]; /* (char)0 is stored as 'I'; convert back */ for (i = 0; i < 4; i++) c[i] = (ip->sb_buf[i+1] == 'I' ? 0 : ip->sb_buf[i+1]); w = ((unsigned char)c[0]) * 256 + ((unsigned char)c[1]); h = ((unsigned char)c[2]) * 256 + ((unsigned char)c[3]); push_number(w); push_number(h); apply(APPLY_WINDOW_SIZE, ip->ob, 2, ORIGIN_DRIVER); } else { push_constant_string(ip->sb_buf); apply(APPLY_TELNET_SUBOPTION, ip->ob, 1, ORIGIN_DRIVER); } ip->state = TS_DATA; break; case DM: default: ip->state = TS_DATA; break; } break; case TS_DO: if (from[i] == TELOPT_TM) { add_message(ip->ob, telnet_do_tm_response); flush_message(ip); } ip->state = TS_DATA; break; case TS_WILL: if (from[i] == TELOPT_TTYPE) { add_message(ip->ob, telnet_term_query); flush_message(ip); } case TS_DONT: case TS_WONT: ip->state = TS_DATA; break; case TS_SB: if ((unsigned char) from[i] == IAC) { ip->state = TS_SB_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); } /* copy_chars() */ /* * SIGPIPE handler -- does very little for now. */ #ifdef SIGNAL_FUNC_TAKES_INT static void sigpipe_handler P1(int, sig) #else static void sigpipe_handler() #endif { debug_message("SIGPIPE received.\n"); signal(SIGPIPE, sigpipe_handler); } /* sigpipe_handler() */ /* * SIGALRM handler. */ #ifdef SIGNAL_FUNC_TAKES_INT void sigalrm_handler P1(int, sig) #else void sigalrm_handler() #endif { heart_beat_flag = 1; debug(512, ("sigalrm_handler: SIGALRM\n")); } /* sigalrm_handler() */ 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. */ for (i = 0; i < 5; i++) { if (!external_port[i].port) continue; FD_SET(external_port[i].fd, &readmask); } /* * 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]->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); } #ifdef PACKAGE_SOCKETS /* * 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 } /* make_selectmasks() */ /* * Process I/O. */ INLINE void process_io() { int i; debug(256, ("@")); /* * 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(512, ("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); 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)) flush_message(all_users[i]); } #ifdef PACKAGE_SOCKETS /* * 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. */ static void new_user_handler P1(int, which) { int new_socket_fd; struct sockaddr_in addr; int length; int i; object_t *ob; svalue_t *ret; length = sizeof(addr); debug(512, ("new_user_handler: accept on fd %d\n", new_user_fd)); new_socket_fd = accept(external_port[which].fd, (struct sockaddr *) & addr, (int *) &length); if (new_socket_fd < 0) { #ifdef WINSOCK if (errno == WSAEWOULDBLOCK) { #else if (errno == EWOULDBLOCK) { #endif debug(512, ("new_user_handler: accept: Operation would block\n")); } else { debug_perror("new_user_handler: accept", 0); } return; } #ifdef linux /* * according to Amylaar, 'accepted' sockets in Linux 0.99p6 don't * properly inherit the nonblocking property from the listening socket. */ if (set_socket_nonblocking(new_socket_fd, 1) == -1) { debug_perror("new_user_handler: set_socket_nonblocking 1", 0); exit(8); } #endif /* linux */ 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; } command_giver = master_ob; master_ob->interactive = (interactive_t *) DXALLOC(sizeof(interactive_t), TAG_INTERACTIVE, "new_user_handler"); total_users++; #ifndef NO_ADD_ACTION master_ob->interactive->default_err_message.s = 0; #endif master_ob->interactive->connection_type = external_port[which].kind; 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->iflags = 0; master_ob->interactive->text[0] = '\0'; master_ob->interactive->text_end = 0; master_ob->interactive->text_start = 0; master_ob->interactive->carryover = NULL; master_ob->interactive->snoop_on = 0; master_ob->interactive->snoop_by = 0; 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 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->state = TS_DATA; master_ob->interactive->out_of_band = 0; 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 set_prompt("> "); memcpy((char *) &all_users[i]->addr, (char *) &addr, length); debug(512, ("New connection from %s.\n", inet_ntoa(addr.sin_addr))); num_user++; /* * The user object has one extra reference. It is asserted that the * master_ob is loaded. */ add_ref(master_ob, "new_user"); push_number(external_port[which].port); ret = apply_master_ob(APPLY_CONNECT, 1); if (ret == 0 || ret == (svalue_t *)-1 || ret->type != T_OBJECT) { remove_interactive(master_ob); debug_message("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->iflags |= (HAS_WRITE_PROMPT | HAS_PROCESS_INPUT); 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); } if (external_port[which].kind == PORT_TELNET) { /* Ask permission to ask them for their terminal type */ add_message(ob, telnet_do_ttype); /* Ask them for their window size */ add_message(ob, telnet_do_naws); } logon(ob); debug(512, ("new_user_handler: end\n")); command_giver = 0; } /* new_user_handler() */ /* * 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; object_t *save_current_object = current_object; object_t *save_command_giver = command_giver; svalue_t *ret; buf[MAX_TEXT - 1] = '\0'; if ((user_command = get_user_command())) { if (command_giver->flags & O_DESTRUCTED) { command_giver = save_command_giver; current_object = save_current_object; return (1); } #ifndef NO_ADD_ACTION clear_notify(); /* moved from user_parser() */ #endif 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] == '!') && ( #ifdef OLD_ED command_giver->interactive->ed_buffer || #endif (command_giver->interactive->input_to && !(command_giver->interactive->iflags & NOESC)))) { if (command_giver->interactive->iflags & SINGLE_CHAR) { /* only 1 char ... switch to line buffer mode */ command_giver->interactive->iflags |= WAS_SINGLE_CHAR; command_giver->interactive->iflags &= ~SINGLE_CHAR; add_message(command_giver, telnet_no_single); /* come back later */ } else { if (command_giver->interactive->iflags & WAS_SINGLE_CHAR) { /* we now have a string ... switch back to char mode */ command_giver->interactive->iflags &= ~WAS_SINGLE_CHAR; command_giver->interactive->iflags |= SINGLE_CHAR; add_message(command_giver, telnet_yes_single); } if (command_giver->interactive->iflags & HAS_PROCESS_INPUT) { push_constant_string(user_command + 1); /* not malloc'ed */ ret = apply(APPLY_PROCESS_INPUT, command_giver, 1, ORIGIN_DRIVER); if (!command_giver || (command_giver->flags & O_DESTRUCTED)) { command_giver = save_command_giver; current_object = save_current_object; return 1; } if (!ret && command_giver->interactive) command_giver->interactive->iflags &= ~HAS_PROCESS_INPUT; #ifndef NO_ADD_ACTION if (ret && ret->type == T_STRING) { strncpy(buf, ret->u.string, MAX_TEXT - 1); parse_command(buf, command_giver); } else if (!ret || ret->type != T_NUMBER || !ret->u.number) { parse_command(tbuf+1, command_giver); } #endif } #ifndef NO_ADD_ACTION else parse_command(tbuf + 1, command_giver); #endif } #ifdef OLD_ED } else if (command_giver->interactive->ed_buffer) { 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->iflags & HAS_PROCESS_INPUT) { push_constant_string(user_command); /* not malloc'ed */ ret = apply(APPLY_PROCESS_INPUT, command_giver, 1, ORIGIN_DRIVER); if (!command_giver || command_giver->flags & O_DESTRUCTED) { command_giver = save_command_giver; current_object = save_current_object; return 1; } if (!ret && command_giver->interactive) command_giver->interactive->iflags &= ~HAS_PROCESS_INPUT; #ifndef NO_ADD_ACTION if (ret && ret->type == T_STRING) { strncpy(buf, ret->u.string, MAX_TEXT - 1); parse_command(buf, command_giver); } else if (!ret || ret->type != T_NUMBER || !ret->u.number) { parse_command(tbuf, command_giver); } #endif } #ifndef NO_ADD_ACTION else parse_command(tbuf, command_giver); #endif } /* * 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; current_interactive = 0; return (0); } /* process_user_command() */ #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]; int num_bytes; int tmp; char *pp, *q; long laddr; if (addr_server_fd < 0) { return; } num_bytes = OS_socket_read(addr_server_fd, hname_buf, HNAME_BUF_SIZE); switch (num_bytes) { case -1: switch (errno) { #ifdef EWOULDBLOCK case EWOULDBLOCK: debug(512, ("hname_handler: read on fd %d: Operation would block.\n", addr_server_fd)); break; #endif #ifdef WSAEWOULDBLOCK case WSAEWOULDBLOCK: debug(512, ("hname_handler: read on fd %d: Operation would block.\n", addr_server_fd)); break; #endif default: debug_message("hname_handler: read on fd %d\n", addr_server_fd); debug_perror("hname_handler: read", 0); tmp = addr_server_fd; addr_server_fd = -1; OS_socket_close(tmp); return; } break; case 0: debug_message("hname_handler: closing address server connection.\n"); tmp = addr_server_fd; addr_server_fd = -1; OS_socket_close(tmp); return; 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) { *pp = 0; pp++; q = strchr(pp, '\n'); 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; } } /* hname_handler() */ /* * Read pending data for a user into user->interactive->text. * This also does telnet negotiation. */ static void get_user_data P1(interactive_t *, ip) { static char buf[MAX_TEXT]; int text_space; int num_bytes; #ifdef DEBUG_COMM_FREEZE int i; #endif /* * this /3 is here because of the trick copy_chars() uses to allow empty * commands. it needs to be fixed right. later. */ if (ip->connection_type == PORT_TELNET) { text_space = (MAX_TEXT - ip->text_end - 1) / 3; } else { text_space = sizeof(buf) - 1; } /* * Check if we need more space. */ if (text_space < MAX_TEXT/16) { int l = ip->text_end - ip->text_start; memmove(ip->text, ip->text + ip->text_start, l + 1); ip->text_start = 0; ip->text_end = l; text_space = (MAX_TEXT - ip->text_end - 1) / 3; if (text_space < MAX_TEXT/16) { /* almost 2k data without a newline. Flush it, otherwise text_space will eventually go to zero and dest the user. */ ip->text_start = 0; ip->text_end = 0; text_space = MAX_TEXT / 3; } } /* * read user data. */ debug(512, ("get_user_data: read on fd %d\n", ip->fd)); num_bytes = OS_socket_read(ip->fd, buf, text_space); #ifdef DEBUG_COMM_FREEZE /* slow, but it's debugging code */ for (i=0; i<1024; i++) { ip->debug_block[i] = buf[i]; } ip->debug_block_size = num_bytes; #endif switch (num_bytes) { case 0: if (ip->iflags & CLOSING) debug_message("get_user_data: tried to read from closing fd.\n"); remove_interactive(ip->ob); return; case -1: #ifdef EWOULDBLOCK if (errno == EWOULDBLOCK) { debug(512, ("get_user_data: read on fd %d: Operation would block.\n", ip->fd)); } else #endif #ifdef WSAEWOULDBLOCK if (errno == WSAEWOULDBLOCK) { debug(512, ("get_user_data: read on fd %d: Operation would block.\n", ip->fd)); } else #endif { debug_message("get_user_data: read on fd %d\n", ip->fd); debug_perror("get_user_data: read", 0); remove_interactive(ip->ob); return; } break; default: buf[num_bytes] = '\0'; switch (ip->connection_type) { case PORT_TELNET: /* * 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((unsigned char *)buf, (unsigned char *)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->iflags & NOECHO)) receive_snoop(buf, ip->snoop_by->ob); /* * set flag if new data completes command. */ if (cmd_in_buf(ip)) ip->iflags |= CMD_IN_BUF; break; case PORT_ASCII: { char temp[2 * MESSAGE_BUF_SIZE]; int old_num = ip->text_end - ip->text_start; char *p, *nl; svalue_t *ret; memcpy(temp, ip->text + ip->text_start, old_num); memcpy(temp + old_num, buf, num_bytes); temp[num_bytes + old_num] = 0; p = temp; while ((nl = strchr(p, '\n'))) { *nl = 0; if (!(ip->ob->flags & O_DESTRUCTED)) { push_string(p, STRING_MALLOC); ret = apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER); } p = nl + 1; } num_bytes = strlen(p); ip->text_start = 0; ip->text_end = num_bytes; memcpy(ip->text, p, num_bytes); break; } case PORT_BINARY: { buffer_t *buffer; svalue_t *ret; buffer = allocate_buffer(num_bytes); memcpy(buffer->item, buf, num_bytes); push_refed_buffer(buffer); ret = apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER); break; } } } } /* get_user_data() */ /* * 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 = 0; #ifdef DEBUG_COMM_FREEZE static char *debug_dump P2(char *, block, int, size) { char buffer[4096]; char *bufp = buffer; int i=0; if (size > 1023) size = 1023; while (i<size) { if (block[i]<32 || block[i]>127) { *bufp++='\\'; if (block[i]>99) *bufp++ = (block[i]/100) + '0'; if (block[i]>9) *bufp++ = (block[i] %100)/10 + '0'; *bufp++ = block[i]%10 + '0'; } else { switch(block[i]) { case '\\': *bufp++ = '\\'; default: *bufp++ = block[i]; } } } *bufp = 0; return buffer; } #endif static char *get_user_command() { int i; interactive_t *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->iflags & CMD_IN_BUF)) { user_command = first_cmd_in_buf(ip); if (user_command) break; #ifdef DEBUG_COMM_FREEZE else { debug_message("*********************\nFrozen user found.\n"); debug_message("Last Block = %s\n\n", debug_dump(ip->debug_block, ip->debug_block_size)); ip->iflags &= ~CMD_IN_BUF; } #else else ip->iflags &= ~CMD_IN_BUF; #endif } 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->iflags &= ~CMD_IN_BUF; IncCmdGiver; if (ip->iflags & NOECHO) { /* * Must not enable echo before the user input is received. */ add_message(command_giver, telnet_no_echo); ip->iflags &= ~NOECHO; } ip->last_time = current_time; return (buf); } /* get_user_command() */ /* * 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. */ static char *first_cmd_in_buf P1(interactive_t *, 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->iflags & 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); } /* first_command_in_buf() */ /* * return(1) if there is a complete command in ip->text, otherwise return(0). */ static int cmd_in_buf P1(interactive_t *, 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->iflags & 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); } /* cmd_in_buf() */ /* * move pointers to next cmd, or clear buf. */ static void next_cmd_in_buf P1(interactive_t *, 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'; } } /* next_cmd_in_buf() */ /* * Remove an interactive user immediately. */ void remove_interactive P1(object_t *, ob) { int i; if (!ob->interactive) { return; } for (i = 0; i < max_users; i++) { if (all_users[i] != ob->interactive) continue; if (ob->interactive->iflags & CLOSING) { debug_message("Double call to remove_interactive()\n"); return; } ob->interactive->iflags |= CLOSING; /* * auto-notification of net death */ safe_apply(APPLY_NET_DEAD, ob, 0, ORIGIN_DRIVER); 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; } debug(512, ("Closing connection from %s.\n", inet_ntoa(ob->interactive->addr.sin_addr))); #ifdef F_ED if (ob->interactive->ed_buffer) { save_ed_buffer(ob); } #endif debug(512, ("remove_interactive: closing fd %d\n", ob->interactive->fd)); if (OS_socket_close(ob->interactive->fd) == -1) { debug_perror("remove_interactive: close", 0); } if (ob->flags & O_HIDDEN) num_hidden--; num_user--; #ifndef NO_ADD_ACTION clear_notify(); #endif 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"); return; } fatal("remove_interactive: could not find and remove user %s\n", ob->name); } /* remove_interactive() */ static int call_function_interactive P2(interactive_t *, i, char *, str) { object_t *ob; funptr_t *funp; char *function; svalue_t *args; sentence_t *sent; int num_arg; 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; 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(sent->ob, "call_function_interactive"); /* we put the function on the stack in case of an error */ sp++; 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_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; add_message(i->ob, telnet_no_single); } push_constant_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) (void) apply(function, ob, num_arg + 1, ORIGIN_DRIVER); else call_function_pointer(funp, num_arg + 1); pop_stack(); /* remove `function' from stack */ return (1); } /* call_function_interactive() */ int set_call P3(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_message(ob, telnet_yes_echo); if (flags & I_SINGLE_CHAR) add_message(ob, telnet_yes_single); return (1); } /* set_call() */ void set_prompt P1(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() { if (command_giver->interactive->input_to == 0) { /* give user object a chance to write its own prompt */ if (!(command_giver->interactive->iflags & HAS_WRITE_PROMPT)) tell_object(command_giver, command_giver->interactive->prompt); #ifdef OLD_ED else if (command_giver->interactive && command_giver->interactive->ed_buffer) tell_object(command_giver, command_giver->interactive->prompt); #endif else if (!(command_giver->flags & O_DESTRUCTED) && !apply(APPLY_WRITE_PROMPT, command_giver, 0, ORIGIN_DRIVER)) { if (command_giver->interactive) { command_giver->interactive->iflags &= ~HAS_WRITE_PROMPT; tell_object(command_giver, command_giver->interactive->prompt); } } } /* * 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 (command_giver->interactive->iflags & USING_TELNET) add_message(command_giver, telnet_ga); } /* 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. */ int new_set_snoop P2(object_t *, me, object_t *, you) { interactive_t *on, *by, *tmp; /* * 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. */ if (!me->interactive) error("First argument of snoop() is not interactive!\n"); by = me->interactive; if (you) { if (!you->interactive) error("Second argument of snoop() is not interactive!\n"); on = you->interactive; } else { /* * Stop snoop. */ if (by->snoop_on) { by->snoop_on->snoop_by = 0; by->snoop_on = 0; } return 1; } /* * 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); } /* set_new_snoop() */ /* * Bit of a misnomer now. But I can't be bothered changeing the * name. This will handle backspace resolution amongst other things, * (Pinkfish change) */ static void telnet_neg P2(char *, to, char *, 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() */ static void query_addr_name P1(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(512, ("query_addr_name: sent address server %s\n", dbuf)); if (OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int)) == -1) { switch (errno) { case EBADF: debug_message("Address server has closed connection.\n"); addr_server_fd = -1; break; default: debug_perror("query_addr_name: write", 0); break; } } } /* query_addr_name() */ #define IPSIZE 200 typedef struct { char *name, *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 P2(char *, name, char *, call_back) { static char buf[100]; static char *dbuf = &buf[sizeof(int) + sizeof(int) + sizeof(int)]; int msglen; int msgtype; if ((addr_server_fd < 0) || (strlen(name) >= 100 - (sizeof(msgtype) + sizeof(msglen) + sizeof(int)))) { push_constant_string(name); push_null(); apply(call_back, current_object, 2, ORIGIN_DRIVER); 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 = (name[0] >= '0' && name[0] <= '9') ? NAMEBYIP : IPBYNAME; memcpy(&buf[sizeof(int) + sizeof(int)], (char *) &msgtype, sizeof(msgtype)); debug(512, ("query_addr_number: sent address server %s\n", dbuf)); if (OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int)) == -1) { switch (errno) { case EBADF: debug_message("Address server has closed connection.\n"); addr_server_fd = -1; break; default: debug_perror("query_addr_name: write", 0); break; } push_constant_string(name); push_null(); apply(call_back, current_object, 2, ORIGIN_DRIVER); 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, ORIGIN_DRIVER); 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() */ static void got_addr_number P2(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_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; i++) { if (ipnumbertable[i].name && strcmp(name, ipnumbertable[i].name)== 0) { /* Found one, do the call back... */ 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, ORIGIN_DRIVER); 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 typedef struct { long addr; 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 char *query_ip_name P1(object_t *, 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 P2(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 P1(object_t *, 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 P1(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[40]; gethostname(name, sizeof(name)); name[sizeof(name) - 1] = '\0'; /* Just to make sure */ return (name); } /* query_host_name() */ object_t *query_snoop P1(object_t *, ob) { if (!ob->interactive || (ob->interactive->snoop_by == 0)) return (0); return (ob->interactive->snoop_by->ob); } /* query_snoop() */ object_t *query_snooping P1(object_t *, ob) { if (!ob->interactive || (ob->interactive->snoop_on == 0)) return (0); return (ob->interactive->snoop_on->ob); } /* query_snooping() */ int query_idle P1(object_t *, ob) { if (!ob->interactive) error("query_idle() of non-interactive object.\n"); return (current_time - ob->interactive->last_time); } /* query_idle() */ #ifndef NO_ADD_ACTION void notify_no_command() { union string_or_func p; svalue_t *v; if (!command_giver || !command_giver->interactive) return; p = command_giver->interactive->default_err_message; if (command_giver->interactive->iflags & NOTIFY_FAIL_FUNC) { v = call_function_pointer(p.f, 0); free_funp(p.f); if (v && v->type == T_STRING) { tell_object(command_giver, v->u.string); } command_giver->interactive->iflags &= ~NOTIFY_FAIL_FUNC; command_giver->interactive->default_err_message.s = 0; } else { if (p.s) { tell_object(command_giver, p.s); free_string(p.s); command_giver->interactive->default_err_message.s = 0; } else { tell_object(command_giver, default_fail_message); tell_object(command_giver, "\n"); } } } /* notify_no_command() */ static void clear_notify() { union string_or_func dem; if (!command_giver || !command_giver->interactive) return; dem = command_giver->interactive->default_err_message; if (command_giver->interactive->iflags & NOTIFY_FAIL_FUNC) { free_funp(dem.f); command_giver->interactive->iflags &= ~NOTIFY_FAIL_FUNC; } else if (dem.s) free_string(dem.s); command_giver->interactive->default_err_message.s = 0; } /* clear_notify() */ void set_notify_fail_message P1(char *, str) { if (!command_giver || !command_giver->interactive) return; clear_notify(); command_giver->interactive->default_err_message.s = make_shared_string(str); } /* set_notify_fail_message() */ void set_notify_fail_function P1(funptr_t *, fp) { if (!command_giver || !command_giver->interactive) return; clear_notify(); command_giver->interactive->iflags |= NOTIFY_FAIL_FUNC; command_giver->interactive->default_err_message.f = fp; fp->hdr.ref++; } /* set_notify_fail_message() */ #endif /* NO_ADD_ACTION */ int replace_interactive P2(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"); } 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->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"); free_object(obfrom, "exec"); if (obfrom == command_giver) { command_giver = ob; } return (1); } /* replace_interactive() */ void outbuf_zero P1(outbuffer_t *, outbuf) { outbuf->real_size = 0; outbuf->buffer = 0; } int outbuf_extend P2(outbuffer_t *, outbuf, int, l) { int limit; if (outbuf->buffer) { limit = MSTR_SIZE(outbuf->buffer); if (outbuf->real_size + l > limit) { if (outbuf->real_size == USHRT_MAX) return 0; /* TRUNCATED */ /* assume it's going to grow some more */ limit = (outbuf->real_size + l) * 2; if (limit > USHRT_MAX) { limit = outbuf->real_size + l; if (limit > USHRT_MAX) { outbuf->buffer = extend_string(outbuf->buffer, USHRT_MAX); return USHRT_MAX - outbuf->real_size; } } outbuf->buffer = extend_string(outbuf->buffer, limit); } } else { outbuf->buffer = new_string(l, "outbuf_add"); outbuf->real_size = 0; } return l; } void outbuf_add P2(outbuffer_t *, outbuf, char *, str) { int l, limit; if (!outbuf) return; l = strlen(str); if (outbuf->buffer) { limit = MSTR_SIZE(outbuf->buffer); if (outbuf->real_size + l > limit) { if (outbuf->real_size == USHRT_MAX) return; /* TRUNCATED */ /* assume it's going to grow some more */ limit = (outbuf->real_size + l) * 2; if (limit > USHRT_MAX) { limit = outbuf->real_size + l; if (limit > USHRT_MAX) { outbuf->buffer = extend_string(outbuf->buffer, USHRT_MAX); strncpy(outbuf->buffer + outbuf->real_size, str, USHRT_MAX - outbuf->real_size); outbuf->buffer[USHRT_MAX] = 0; outbuf->real_size = USHRT_MAX; return; } } outbuf->buffer = extend_string(outbuf->buffer, limit); } } else { outbuf->buffer = new_string(l, "outbuf_add"); outbuf->real_size = 0; } strcpy(outbuf->buffer + outbuf->real_size, str); outbuf->real_size += l; } void outbuf_addchar P2(outbuffer_t *, outbuf, char, c) { int limit; if (!outbuf) return; if (outbuf->buffer) { limit = MSTR_SIZE(outbuf->buffer); if (outbuf->real_size + 1 > limit) { if (outbuf->real_size == USHRT_MAX) return; /* TRUNCATED */ /* assume it's going to grow some more */ limit = (outbuf->real_size + 1) * 2; if (limit > USHRT_MAX) { limit = outbuf->real_size + 1; if (limit > USHRT_MAX) { outbuf->buffer = extend_string(outbuf->buffer, USHRT_MAX); *(outbuf->buffer + outbuf->real_size) = c; outbuf->buffer[USHRT_MAX] = 0; outbuf->real_size = USHRT_MAX; return; } } outbuf->buffer = extend_string(outbuf->buffer, limit); } } else { outbuf->buffer = new_string(80, "outbuf_add"); outbuf->real_size = 0; } *(outbuf->buffer + outbuf->real_size++) = c; *(outbuf->buffer + outbuf->real_size) = 0; } void outbuf_addv P2V(outbuffer_t *, outbuf, char *, format) { char buf[LARGEST_PRINTABLE_STRING]; va_list args; V_DCL(char *format); V_DCL(outbuffer_t *outbuf); V_START(args, format); V_VAR(outbuffer_t *, outbuf, args); V_VAR(char *, format, args); vsprintf(buf, format, args); va_end(args); if (!outbuf) return; outbuf_add(outbuf, buf); } void outbuf_fix P1(outbuffer_t *, outbuf) { if (outbuf && outbuf->buffer) outbuf->buffer = extend_string(outbuf->buffer, outbuf->real_size); } void outbuf_push P1(outbuffer_t *, outbuf) { (++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 = ""; } }