/* * comm.c -- communications functions and more. * Dwayne Fontenot (Jacques@TMI) */ #include "std.h" #include "comm.h" #include "main.h" #include "socket_efuns.h" #include "backend.h" #include "socket_ctrl.h" #include "debug.h" #include "ed.h" #include "file.h" #include "master.h" #include "add_action.h" static char telnet_break_response[] = { 28, IAC, WILL, TELOPT_TM }; static char telnet_ip_response[] = { 127, IAC, WILL, TELOPT_TM }; static char telnet_abort_response[] = { IAC, DM }; static char telnet_do_tm_response[] = { IAC, WILL, TELOPT_TM }; static char telnet_do_naws[] = { IAC, DO, TELOPT_NAWS }; static char telnet_do_ttype[] = { IAC, DO, TELOPT_TTYPE }; static char telnet_term_query[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; static char telnet_no_echo[] = { IAC, WONT, TELOPT_ECHO }; static char telnet_no_single[] = { IAC, WONT, TELOPT_SGA }; static char telnet_yes_echo[] = { IAC, WILL, TELOPT_ECHO }; static char telnet_yes_single[] = { IAC, WILL, TELOPT_SGA }; static char telnet_ga[] = { IAC, GA }; static char telnet_ayt_response[] = { '\n', '[', '-', 'Y', 'e', 's', '-', ']', ' ', '\n' }; static char telnet_line_mode[] = { IAC, DO, TELOPT_LINEMODE }; static char telnet_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT | MODE_TRAPSIG, IAC, SE }; static char telnet_char_mode[] = { IAC, DONT, TELOPT_LINEMODE }; static char slc_default_flags[] = { SLC_NOSUPPORT, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_CANTCHANGE, SLC_CANTCHANGE, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT, SLC_NOSUPPORT }; static char slc_default_chars[] = { 0x00, BREAK, IP, AO, AYT, 0x00, 0x00, 0x00, SUSP, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #ifdef DEBUG # ifndef sgi char *slc_names[] = { SLC_NAMELIST }; #endif #endif /* * local function prototypes. */ #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 int call_function_interactive PROT((interactive_t *, char *)); static void print_prompt PROT((interactive_t *)); static void query_addr_name PROT((object_t *)); static void got_addr_number PROT((char *, char *)); static void add_ip_entry PROT((long, char *)); static void new_user_handler PROT((int)); #ifdef NO_SNOOP # define handle_snoop(str, len, who) #else # define handle_snoop(str, len, who) if ((who)->snooped_by) receive_snoop(str, len, who->snooped_by) static void receive_snoop PROT((char *, int, object_t * ob)); #endif /* * public local variables. */ fd_set readmask, writemask; int num_user; #ifdef F_SET_HIDE int num_hidden_users = 0; /* for the O_HIDDEN flag. This counter must * be kept up to date at all times! If you * modify the O_HIDDEN flag in an object, * make sure that you update this counter if * the object is interactive. */ #endif int add_message_calls = 0; #ifdef F_NETWORK_STATS int inet_out_packets = 0; int inet_out_volume = 0; int inet_in_packets = 0; int inet_in_volume = 0; #ifdef PACKAGE_SOCKETS int inet_socket_in_packets = 0; int inet_socket_in_volume = 0; int inet_socket_out_packets = 0; int inet_socket_out_volume = 0; #endif #endif int inet_packets = 0; int inet_volume = 0; interactive_t **all_users = 0; int max_users = 0; /* * private local variables. */ static int addr_server_fd = -1; static void set_linemode P1(interactive_t *, ip) { if (ip->iflags & USING_LINEMODE) { add_binary_message(ip->ob, telnet_line_mode, sizeof(telnet_line_mode)); add_binary_message(ip->ob, telnet_lm_mode, sizeof(telnet_lm_mode)); } else { add_binary_message(ip->ob, telnet_no_single, sizeof(telnet_no_single)); } } static void set_charmode P1(interactive_t *, ip) { if (ip->iflags & USING_LINEMODE) { add_binary_message(ip->ob, telnet_char_mode, sizeof(telnet_char_mode)); } else { add_binary_message(ip->ob, telnet_yes_single, sizeof(telnet_yes_single)); } } #ifndef NO_SNOOP static void receive_snoop P3(char *, buf, int, len, object_t *, snooper) { /* command giver no longer set to snooper */ #ifdef RECEIVE_SNOOP char *str; str = new_string(len, "receive_snoop"); memcpy(str, buf, len); str[len] = 0; push_malloced_string(str); apply(APPLY_RECEIVE_SNOOP, snooper, 1, ORIGIN_DRIVER); #else /* snoop output is now % in all cases */ add_message(snooper, "%", 1); add_message(snooper, buf, len); #endif } #endif /* * Initialize new user connection socket. */ void init_user_conn() { struct sockaddr_in sin; int sin_len; int optval; int i; int have_fd6; int fd6_which; /* Check for fd #6 open as a valid socket */ optval = 1; have_fd6 = (setsockopt(6, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == 0); for (i=0; i < 5; i++) { #ifdef F_NETWORK_STATS external_port[i].in_packets = 0; external_port[i].in_volume = 0; external_port[i].out_packets = 0; external_port[i].out_volume = 0; #endif if (!external_port[i].port) { if (!have_fd6) continue; fd6_which = i; have_fd6 = 0; if (FD6_KIND == PORT_UNDEFINED || FD6_PORT < 1) { debug_message("Socket passed to fd 6 ignored (support is disabled).\n"); continue; } debug_message("Accepting connections on fd 6 (port %d).\n", FD6_PORT); external_port[i].kind = FD6_KIND; external_port[i].port = FD6_PORT; external_port[i].fd = 6; } else { /* * create socket of proper type. */ if ((external_port[i].fd = socket(PF_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) { socket_perror("init_user_conn: setsockopt", 0); exit(2); } /* * fill in socket address information. */ sin.sin_family = AF_INET; if (MUD_IP[0]) sin.sin_addr.s_addr = inet_addr(MUD_IP); else sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons((u_short) external_port[i].port); /* * bind name to socket. */ if (bind(external_port[i].fd, (struct sockaddr *) & sin, sizeof(sin)) == -1) { socket_perror("init_user_conn: bind", 0); exit(3); } } /* * get socket name. */ sin_len = sizeof(sin); if (getsockname(external_port[i].fd, (struct sockaddr *) & sin, &sin_len) == -1) { socket_perror("init_user_conn: getsockname", 0); if (i != fd6_which) { exit(4); } } /* * set socket non-blocking, */ if (set_socket_nonblocking(external_port[i].fd, 1) == -1) { socket_perror("init_user_conn: set_socket_nonblocking 1", 0); if (i != fd6_which) { exit(8); } } /* * listen on socket for connections. */ if (listen(external_port[i].fd, 128) == -1) { socket_perror("init_user_conn: listen", 0); if (i != fd6_which) { exit(10); } } } if (have_fd6) { debug_message("No more ports available; fd #6 ignored.\n"); } /* * register signal handler for SIGPIPE. */ #if !defined(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) { socket_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 (addr_server_fd >= 0) return; if (!hostname) return; /* * get network host data for hostname. */ if (hostname[0] >= '0' && hostname[0] <= '9' && (addr = inet_addr(hostname)) != -1) { hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); } else { hp = gethostbyname(hostname); } if (hp == NULL) { socket_perror("init_addr_server: gethostbyname", 0); return; } /* * set up address information for server. */ server.sin_family = AF_INET; server.sin_port = htons((u_short) addr_server_port); server.sin_addr.s_addr = inet_addr(hostname); memcpy((char *) &server.sin_addr, (char *) hp->h_addr, hp->h_length); /* * create socket of proper type. */ server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd == INVALID_SOCKET) { /* problem opening socket */ socket_perror("init_addr_server: socket", 0); return; } /* * enable local address reuse. */ optval = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof(optval)) == -1) { socket_perror("init_addr_server: setsockopt", 0); return; } /* * connect socket to server address. */ if (connect(server_fd, (struct sockaddr *) & server, sizeof(server)) == -1) { if (socket_errno == ECONNREFUSED) debug_message("Connection to address server (%s %d) refused.\n", hostname, addr_server_port); else socket_perror("init_addr_server: connect", 0); OS_socket_close(server_fd); return; } addr_server_fd = server_fd; debug_message("Connected to address server on %s port %d\n", hostname, addr_server_port); /* * set socket non-blocking. */ if (set_socket_nonblocking(server_fd, 1) == -1) { socket_perror("init_addr_server: set_socket_nonblocking 1", 0); return; } } /* * If there is a shadow for this object, then the message should be * sent to it. But only if catch_tell() is defined. Beware that one of the * shadows may be the originator of the message, which means that we must * not send the message to that shadow, or any shadows in the linked list * before that shadow. * * Also note that we don't need to do this in the case of * INTERACTIVE_CATCH_TELL, since catch_tell() was already called * _instead of_ add_message(), and shadows got their chance then. */ #if !defined(INTERACTIVE_CATCH_TELL) && !defined(NO_SHADOWS) #define SHADOW_CATCH_MESSAGE #endif #ifdef SHADOW_CATCH_MESSAGE static int shadow_catch_message P2(object_t *, ob, char *, str) { if (!ob->shadowed) return 0; while (ob->shadowed != 0 && ob->shadowed != current_object) ob = ob->shadowed; while (ob->shadowing) { copy_and_push_string(str); if (apply(APPLY_CATCH_TELL, ob, 1, ORIGIN_DRIVER)) /* this will work, since we know the */ /* function is defined */ return 1; ob = ob->shadowing; } return 0; } #endif /* * Send a message to an interactive object. If that object is shadowed, * special handling is done. */ void add_message P3(object_t *, who, char *, data, int, len) { interactive_t *ip; char *cp; char *end; /* * if who->interactive is not valid, write message on stderr. * (maybe) */ if (!who || (who->flags & O_DESTRUCTED) || !who->interactive || (who->interactive->iflags & (NET_DEAD | CLOSING))) { #ifdef NONINTERACTIVE_STDERR_WRITE putc(']', stderr); fwrite(data, len, 1, stderr); #endif #ifdef LATTICE fflush(stderr); #endif return; } ip = who->interactive; #ifdef SHADOW_CATCH_MESSAGE /* * shadow handling. */ if (shadow_catch_message(who, data)) { #ifdef SNOOP_SHADOWED handle_snoop(data, len, ip); #endif return; } #endif /* NO_SHADOWS */ /* * write message into ip->message_buf. */ end = data + len; for (cp = data; cp < end; cp++) { if (ip->message_length == MESSAGE_BUF_SIZE) { if (!flush_message(ip)) { debug(connections, ("Broken connection during add_message.")); return; } if (ip->message_length == MESSAGE_BUF_SIZE) break; } if (*cp == '\n' #ifndef NO_BUFFER_TYPE && ip->connection_type != PORT_BINARY #endif ) { if (ip->message_length == (MESSAGE_BUF_SIZE - 1)) { if (!flush_message(ip)) { debug(connections, ("Broken connection during add_message.")); return; } if (ip->message_length == (MESSAGE_BUF_SIZE - 1)) break; } ip->message_buf[ip->message_producer] = '\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++; } handle_snoop(data, len, ip); #ifdef FLUSH_OUTPUT_IMMEDIATELY flush_message(ip); #endif add_message_calls++; } /* add_message() */ /* WARNING: this can only handle results < LARGEST_PRINTABLE_STRING in size */ void add_vmessage P2V(object_t *, who, char *, format) { int len; interactive_t *ip; char *cp, new_string_data[LARGEST_PRINTABLE_STRING + 1]; 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); len = strlen(new_string_data); #ifdef SHADOW_CATCH_MESSAGE /* * shadow handling. */ if (shadow_catch_message(who, new_string_data)) { #ifdef SNOOP_SHADOWED handle_snoop(new_string_data, len, ip); #endif return; } #endif /* NO_SHADOWS */ /* * write message into ip->message_buf. */ for (cp = new_string_data; *cp != '\0'; cp++) { if (ip->message_length == MESSAGE_BUF_SIZE) { if (!flush_message(ip)) { debug(connections, ("Broken connection during add_message.")); return; } if (ip->message_length == MESSAGE_BUF_SIZE) break; } if (*cp == '\n') { if (ip->message_length == (MESSAGE_BUF_SIZE - 1)) { if (!flush_message(ip)) { debug(connections, ("Broken connection during add_message.\n")); return; } if (ip->message_length == (MESSAGE_BUF_SIZE - 1)) break; } ip->message_buf[ip->message_producer] = '\r'; ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE; ip->message_length++; } ip->message_buf[ip->message_producer] = *cp; ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE; ip->message_length++; } if (ip->message_length != 0) { if (!flush_message(ip)) { debug(connections, ("Broken connection during add_message.\n")); return; } } handle_snoop(new_string_data, len, ip); #ifdef FLUSH_OUTPUT_IMMEDIATELY flush_message(ip); #endif add_message_calls++; } /* add_message() */ void add_binary_message P3(object_t *, who, char *, data, int, len) { interactive_t *ip; char *cp, *end; /* * if who->interactive is not valid, bail */ if (!who || (who->flags & O_DESTRUCTED) || !who->interactive || (who->interactive->iflags & (NET_DEAD | CLOSING))) { return; } ip = who->interactive; /* * write message into ip->message_buf. */ end = data + len; for (cp = data; cp < end; cp++) { if (ip->message_length == MESSAGE_BUF_SIZE) { if (!flush_message(ip)) { debug(connections, ("Broken connection during add_message.")); return; } if (ip->message_length == MESSAGE_BUF_SIZE) break; } ip->message_buf[ip->message_producer] = *cp; ip->message_producer = (ip->message_producer + 1) % MESSAGE_BUF_SIZE; ip->message_length++; } flush_message(ip); add_message_calls++; } /* * Flush outgoing message buffer of current interactive object. */ int flush_message P1(interactive_t *, ip) { int length, num_bytes; /* * if ip is not valid, do nothing. */ if (!ip || (ip->iflags & (NET_DEAD | CLOSING))) { debug(connections, ("flush_message: invalid target!\n")); return 0; } /* * write ip->message_buf[] to socket. */ while (ip->message_length != 0) { if (ip->message_consumer < ip->message_producer) { length = ip->message_producer - ip->message_consumer; } else { length = MESSAGE_BUF_SIZE - ip->message_consumer; } /* Need to use send to get out of band data num_bytes = write(ip->fd,ip->message_buf + ip->message_consumer,length); */ num_bytes = send(ip->fd, ip->message_buf + ip->message_consumer, length, ip->out_of_band); if (!num_bytes) { ip->iflags |= NET_DEAD; return 0; } if (num_bytes == -1) { #ifdef EWOULDBLOCK if (socket_errno == EWOULDBLOCK) { debug(connections, ("flush_message: write: Operation would block\n")); return 1; #else if (0) { ; #endif } else if (socket_errno == EINTR) { debug(connections, ("flush_message: write: Interrupted system call")); return 1; } else { socket_perror("flush_message: write", 0); ip->iflags |= NET_DEAD; return 0; } } ip->message_consumer = (ip->message_consumer + num_bytes) % MESSAGE_BUF_SIZE; ip->message_length -= num_bytes; ip->out_of_band = 0; inet_packets++; inet_volume += num_bytes; #ifdef F_NETWORK_STATS inet_out_packets++; inet_out_volume += num_bytes; external_port[ip->external_port].out_packets++; external_port[ip->external_port].out_volume += num_bytes; #endif } return 1; } /* flush_message() */ static void copy_chars P3(interactive_t *, ip, char *, from, int, num_bytes) { int i, start, x; char dont_response[3] = { IAC, DONT, 0 }; char wont_response[3] = { IAC, WONT, 0 }; start = ip->text_end; for (i = 0; i < num_bytes; i++) { switch (ip->state) { case TS_DATA: switch ((unsigned char)from[i]) { case IAC: ip->state = TS_IAC; break; #if defined(NO_ANSI) && defined(STRIP_BEFORE_PROCESS_INPUT) case 0x1b: ip->text[ip->text_end++] = 0x20; break; #endif case 0x08: case 0x7f: if (ip->iflags & SINGLE_CHAR) ip->text[ip->text_end++] = from[i]; else { if (ip->text_end > 0) ip->text_end--; } break; default: ip->text[ip->text_end++] = from[i]; break; } break; case TS_IAC: switch ((unsigned char)from[i]) { case IAC: ip->state = TS_DATA; ip->text[ip->text_end++] = from[i]; break; case WILL: ip->state = TS_WILL; break; case WONT: ip->state = TS_WONT; break; case DO: ip->state = TS_DO; break; case DONT: ip->state = TS_DONT; break; case SB: ip->state = TS_SB; ip->sb_pos = 0; break; case BREAK: add_binary_message(ip->ob, telnet_break_response, sizeof(telnet_break_response)); break; case IP: /* interrupt process */ add_binary_message(ip->ob, telnet_ip_response, sizeof(telnet_ip_response)); break; case AYT: /* are you there? you bet */ add_binary_message(ip->ob, telnet_ayt_response, sizeof(telnet_ayt_response)); break; case AO: /* abort output */ flush_message(ip); ip->out_of_band = MSG_OOB; add_binary_message(ip->ob, telnet_abort_response, sizeof(telnet_abort_response)); break; default: ip->state = TS_DATA; break; } break; case TS_WILL: ip->iflags |= USING_TELNET; switch ((unsigned char)from[i]) { case TELOPT_TTYPE: add_binary_message(ip->ob, telnet_term_query, sizeof(telnet_term_query)); break; case TELOPT_LINEMODE: /* Do linemode and set the mode: EDIT + TRAPSIG */ ip->iflags |= USING_LINEMODE; set_linemode(ip); break; case TELOPT_ECHO: case TELOPT_NAWS: /* do nothing, but don't send a dont response */ break; default: dont_response[2] = from[i]; add_binary_message(ip->ob, dont_response, sizeof(dont_response)); break; } ip->state = TS_DATA; break; case TS_WONT: ip->iflags |= USING_TELNET; switch ((unsigned char)from[i]) { case TELOPT_LINEMODE: /* If we're in single char mode, we just requested for * linemode to be disabled, so don't remove our flag. */ if (!(ip->iflags & SINGLE_CHAR)) ip->iflags &= ~USING_LINEMODE; break; } ip->state = TS_DATA; break; case TS_DO: switch ((unsigned char)from[i]) { case TELOPT_TM: add_binary_message(ip->ob, telnet_do_tm_response, sizeof(telnet_do_tm_response)); break; case TELOPT_SGA: if (ip->iflags & USING_LINEMODE) { ip->iflags |= SUPPRESS_GA; add_binary_message(ip->ob, telnet_yes_single, sizeof(telnet_yes_single)); } else { if (ip->iflags & SINGLE_CHAR) add_binary_message(ip->ob, telnet_yes_single, sizeof(telnet_yes_single)); else add_binary_message(ip->ob, telnet_no_single, sizeof(telnet_no_single)); } break; case TELOPT_ECHO: /* do nothing, but don't send a wont response */ break; default: wont_response[2] = from[i]; add_binary_message(ip->ob, wont_response, sizeof(wont_response)); break; } ip->state = TS_DATA; break; case TS_DONT: switch ((unsigned char)from[i]) { case TELOPT_SGA: if (ip->iflags & USING_LINEMODE) { ip->iflags &= ~SUPPRESS_GA; add_binary_message(ip->ob, telnet_no_single, sizeof(telnet_no_single)); } break; } ip->state = TS_DATA; break; case TS_SB: if ((unsigned char)from[i] == IAC) { ip->state = TS_SB_IAC; break; } if (ip->sb_pos < SB_SIZE - 1) ip->sb_buf[ip->sb_pos++] = from[i]; break; case TS_SB_IAC: switch ((unsigned char)from[i]) { case IAC: if (ip->sb_pos < SB_SIZE - 1) { ip->sb_buf[ip->sb_pos++] = from[i]; ip->state = TS_SB; } break; case SE: ip->state = TS_DATA; ip->sb_buf[ip->sb_pos] = 0; switch (ip->sb_buf[0]) { case TELOPT_LINEMODE: switch ((unsigned char)ip->sb_buf[1]) { case LM_MODE: /* Don't do anything with an ACK */ if (!(ip->sb_buf[2] & MODE_ACK)) { char sb_ack[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT | MODE_TRAPSIG | MODE_ACK, IAC, SE }; /* Accept only EDIT and TRAPSIG && force them too */ add_binary_message(ip->ob, sb_ack, sizeof(sb_ack)); } break; case LM_SLC: { int slc_length = 4; char slc_response[SB_SIZE + 6] = { IAC, SB, TELOPT_LINEMODE, LM_SLC }; for (x = 2; x < ip->sb_pos; x += 3) { /* no response for an ack */ if (ip->sb_buf[x + 1] & SLC_ACK) continue; /* If we get { 0, SLC_DEFAULT, 0 } or { 0, SLC_VARIABLE, 0 } return a list of values */ /* If it's SLC_DEFAULT, reset to defaults first */ if (!ip->sb_buf[x] && !ip->sb_buf[x + 2]) { if (ip->sb_buf[x + 1] == SLC_DEFAULT || ip->sb_buf[x + 1] == SLC_VARIABLE) { int n; for (n = 0; n < NSLC; n++) { slc_response[slc_length++] = n + 1; if (ip->sb_buf[x + 1] == SLC_DEFAULT) { ip->slc[n][0] = slc_default_flags[n]; ip->slc[n][1] = slc_default_chars[n]; slc_response[slc_length++] = SLC_DEFAULT; } else { slc_response[slc_length++] = ip->slc[n][0]; } slc_response[slc_length++] = ip->slc[n][1]; } break; } } slc_response[slc_length++] = ip->sb_buf[x]--; /* If the first octet is out of range, we don't support it */ /* If the default flag is not supported, we don't support it */ if (ip->sb_buf[x] >= NSLC || slc_default_flags[(int)ip->sb_buf[x]] == SLC_NOSUPPORT) { slc_response[slc_length++] = SLC_NOSUPPORT; slc_response[slc_length++] = ip->sb_buf[x + 2]; if ((unsigned char)ip->sb_buf[x + 2] == IAC) slc_response[slc_length++] = IAC; continue; } switch ((ip->sb_buf[x + 1] & SLC_LEVELBITS)) { case SLC_NOSUPPORT: if (slc_default_flags[(int)ip->sb_buf[x]] == SLC_CANTCHANGE) { slc_response[slc_length++] = SLC_CANTCHANGE; slc_response[slc_length++] = ip->slc[(int)ip->sb_buf[x]][1]; break; } slc_response[slc_length++] = SLC_ACK | SLC_NOSUPPORT; slc_response[slc_length++] = ip->sb_buf[x + 2]; ip->slc[(int)ip->sb_buf[x]][0] = SLC_NOSUPPORT; ip->slc[(int)ip->sb_buf[x]][1] = 0; break; case SLC_VARIABLE: if (slc_default_flags[(int)ip->sb_buf[x]] == SLC_CANTCHANGE) { slc_response[slc_length++] = SLC_CANTCHANGE; slc_response[slc_length++] = ip->slc[(int)ip->sb_buf[x]][1]; break; } slc_response[slc_length++] = SLC_ACK | SLC_VARIABLE; slc_response[slc_length++] = ip->sb_buf[x + 2]; ip->slc[(int)ip->sb_buf[x]][0] = ip->sb_buf[x + 1]; ip->slc[(int)ip->sb_buf[x]][1] = ip->sb_buf[x + 2]; break; case SLC_CANTCHANGE: slc_response[slc_length++] = SLC_ACK | SLC_CANTCHANGE; slc_response[slc_length++] = ip->sb_buf[x + 2]; ip->slc[(int)ip->sb_buf[x]][0] = ip->sb_buf[x + 1]; ip->slc[(int)ip->sb_buf[x]][1] = ip->sb_buf[x + 2]; break; case SLC_DEFAULT: slc_response[slc_length++] = slc_default_flags[(int)ip->sb_buf[x]]; slc_response[slc_length++] = slc_default_flags[(int)ip->sb_buf[x]]; ip->slc[(int)ip->sb_buf[x]][0] = slc_default_flags[(int)ip->sb_buf[x]]; ip->slc[(int)ip->sb_buf[x]][1] = slc_default_chars[(int)ip->sb_buf[x]]; break; default: slc_response[slc_length++] = SLC_NOSUPPORT; slc_response[slc_length++] = ip->sb_buf[x + 2]; if ((unsigned char)slc_response[slc_length - 1] == IAC) slc_response[slc_length++] = IAC; break; } } if (slc_length > 4) { /* send our response */ slc_response[slc_length++] = IAC; slc_response[slc_length++] = SE; add_binary_message(ip->ob, slc_response, slc_length); } } break; case DO: { char sb_wont[] = { IAC, SB, TELOPT_LINEMODE, WONT, 0, IAC, SE }; /* send back IAC SB TELOPT_LINEMODE WONT x IAC SE */ sb_wont[4] = ip->sb_buf[2]; add_binary_message(ip->ob, sb_wont, sizeof(sb_wont)); } break; case WILL: { char sb_dont[] = { IAC, SB, TELOPT_LINEMODE, DONT, 0, IAC, SE }; /* send back IAC SB TELOPT_LINEMODE DONT x IAC SE */ sb_dont[4] = ip->sb_buf[2]; add_binary_message(ip->ob, sb_dont, sizeof(sb_dont)); } break; } break; case TELOPT_NAWS: if (ip->sb_pos >= 5) { push_number(((unsigned char)ip->sb_buf[1] << 8) | (unsigned char)ip->sb_buf[2]); push_number(((unsigned char)ip->sb_buf[3] << 8) | (unsigned char)ip->sb_buf[4]); apply(APPLY_WINDOW_SIZE, ip->ob, 2, ORIGIN_DRIVER); } break; case TELOPT_TTYPE: if (!ip->sb_buf[1]) { copy_and_push_string(ip->sb_buf + 2); apply(APPLY_TERMINAL_TYPE, ip->ob, 1, ORIGIN_DRIVER); } break; default: for (x = 0; x < ip->sb_pos; x++) ip->sb_buf[x] = (ip->sb_buf[x] ? ip->sb_buf[x] : 'I'); copy_and_push_string(ip->sb_buf); apply(APPLY_TELNET_SUBOPTION, ip->ob, 1, ORIGIN_DRIVER); break; } break; default: /* * Apparently, old versions of MudOS would revert to TS_DATA here. * Later versions handle the IAC, and then go back to TS_DATA mode. * I don't think either is proper, but the related RFC documents * aren't clear on what to do (854, 855). It is my feeling, that * the safest thing to do here is to ignore the option. * -- Marius, 6-Jun-2000 */ ip->state = TS_SB; break; } break; } } if (ip->text_end > start) { /* handle snooping - snooper does not see type-ahead due to telnet being in linemode */ if (!(ip->iflags & NOECHO)) handle_snoop(ip->text + start, ip->text_end - start, ip); } } /* * Read pending data for a user into user->interactive->text. * This also does telnet negotiation. */ static void get_user_data P1(interactive_t *, ip) { int num_bytes, text_space; char buf[MAX_TEXT]; /* compute how much data we can read right now */ switch (ip->connection_type) { case PORT_TELNET: text_space = MAX_TEXT - ip->text_end; /* check if we need more space */ if (text_space < MAX_TEXT / 16) { if (ip->text_start > 0) { memmove(ip->text, ip->text + ip->text_start, ip->text_end - ip->text_start); text_space += ip->text_start; ip->text_end -= ip->text_start; ip->text_start = 0; } if (text_space < MAX_TEXT / 16) { /* the user is sending too much data. flush it */ ip->iflags |= SKIP_COMMAND; ip->text_start = ip->text_end = 0; text_space = MAX_TEXT; } } break; case PORT_MUD: if (ip->text_end < 4) text_space = 4 - ip->text_end; else text_space = *(int *)ip->text - ip->text_end + 4; break; default: text_space = sizeof(buf); break; } /* read the data from the socket */ debug(connections, ("get_user_data: read on fd %d\n", ip->fd)); num_bytes = OS_socket_read(ip->fd, buf, text_space); if (!num_bytes) { if (ip->iflags & CLOSING) debug_message("get_user_data: tried to read from closing fd.\n"); ip->iflags |= NET_DEAD; remove_interactive(ip->ob, 0); return; } if (num_bytes == -1) { #ifdef EWOULDBLOCK if (socket_errno == EWOULDBLOCK) { debug(connections, ("get_user_data: read on fd %d: Operation would block.\n", ip->fd)); return; } #endif debug_message("get_user_data: read on fd %d\n", ip->fd); socket_perror("get_user_data: read", 0); ip->iflags |= NET_DEAD; remove_interactive(ip->ob, 0); return; } #ifdef F_NETWORK_STATS inet_in_packets++; inet_in_volume += num_bytes; external_port[ip->external_port].in_packets++; external_port[ip->external_port].in_volume += num_bytes; #endif /* process the data that we've just read */ switch (ip->connection_type) { case PORT_TELNET: copy_chars(ip, buf, num_bytes); if (cmd_in_buf(ip)) ip->iflags |= CMD_IN_BUF; break; case PORT_MUD: memcpy(ip->text + ip->text_end, buf, num_bytes); ip->text_end += num_bytes; if (num_bytes == text_space) { if (ip->text_end == 4) { *(int *)ip->text = ntohl(*(int *)ip->text); if (*(int *)ip->text > MAX_TEXT - 5) remove_interactive(ip->ob, 0); } else { svalue_t value; ip->text[ip->text_end] = 0; if (restore_svalue(ip->text + 4, &value) == 0) { STACK_INC; *sp = value; } else { push_undefined(); } ip->text_end = 0; apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER); } } break; case PORT_ASCII: { char *nl, *p; memcpy(ip->text + ip->text_end, buf, num_bytes); ip->text_end += num_bytes; p = ip->text + ip->text_start; while ((nl = memchr(p, '\n', ip->text_end - ip->text_start))) { ip->text_start = (nl + 1) - ip->text; *nl = 0; if (*(nl - 1) == '\r') *--nl = 0; if (!(ip->ob->flags & O_DESTRUCTED)) { char *str; str = new_string(nl - p, "PORT_ASCII"); memcpy(str, p, nl - p + 1); push_malloced_string(str); apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER); } if (ip->text_start == ip->text_end) { ip->text_start = ip->text_end = 0; break; } p = nl + 1; } } break; #ifndef NO_BUFFER_TYPE case PORT_BINARY: { buffer_t *buffer; buffer = allocate_buffer(num_bytes); memcpy(buffer->item, buf, num_bytes); push_refed_buffer(buffer); apply(APPLY_PROCESS_INPUT, ip->ob, 1, ORIGIN_DRIVER); } break; #endif } } static int clean_buf P1(interactive_t *, ip) { /* skip null input */ while (ip->text_start < ip->text_end && !*(ip->text + ip->text_start)) ip->text_start++; /* if we've advanced beyond the end of the buffer, reset it */ if (ip->text_start >= ip->text_end) { ip->text_start = ip->text_end = 0; } /* if we're skipping the current command, check to see if it has been completed yet. if it has, flush it and clear the skip bit */ if (ip->iflags & SKIP_COMMAND) { char *p; for (p = ip->text + ip->text_start; p < ip->text + ip->text_end; p++) { if (*p == '\r' || *p == '\n') { ip->text_start += p - (ip->text + ip->text_start) + 1; ip->iflags &= ~SKIP_COMMAND; return clean_buf(ip); } } } return (ip->text_end > ip->text_start); } static int cmd_in_buf P1(interactive_t *, ip) { char *p; /* do standard input buffer cleanup */ if (!clean_buf(ip)) return 0; /* if we're in single character mode, we've got input */ if (ip->iflags & SINGLE_CHAR) return 1; /* search for a newline. if found, we have a command */ for (p = ip->text + ip->text_start; p < ip->text + ip->text_end; p++) { if (*p == '\r' || *p == '\n') return 1; } /* duh, no command */ return 0; } static char *first_cmd_in_buf P1(interactive_t *, ip) { char *p; #ifdef GET_CHAR_IS_BUFFERED static char tmp[2]; #endif /* do standard input buffer cleanup */ if (!clean_buf(ip)) return 0; p = ip->text + ip->text_start; /* if we're in single character mode, we've got input */ if (ip->iflags & SINGLE_CHAR) { if (*p == 8 || *p == 127) *p = 0; #ifndef GET_CHAR_IS_BUFFERED ip->text_start++; if (!clean_buf(ip)) ip->iflags &= ~CMD_IN_BUF; return p; #else tmp[0] = *p; ip->text[ip->text_start++] = 0; if (!clean_buf(ip)) ip->iflags &= ~CMD_IN_BUF; return tmp; #endif } /* search for the newline */ while (ip->text[ip->text_start] != '\n' && ip->text[ip->text_start] != '\r') ip->text_start++; /* check for "\r\n" or "\n\r" */ if (ip->text_start + 1 < ip->text_end && ((ip->text[ip->text_start] == '\r' && ip->text[ip->text_start + 1] == '\n') || (ip->text[ip->text_start] == '\n' && ip->text[ip->text_start + 1] == '\r'))) { ip->text[ip->text_start++] = 0; } ip->text[ip->text_start++] = 0; if (!cmd_in_buf(ip)) ip->iflags &= ~CMD_IN_BUF; return p; } /* * SIGPIPE handler -- does very little for now. */ #ifdef SIGNAL_FUNC_TAKES_INT void sigpipe_handler P1(int, sig) #else void sigpipe_handler() #endif { debug(connections, ("SIGPIPE received.")); 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; } /* 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); } #if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL) /* * set fd's for efun sockets. */ for (i = 0; i < max_lpc_socks; i++) { if (lpc_socks[i].state != STATE_CLOSED) { if (lpc_socks[i].state != STATE_FLUSHING && (lpc_socks[i].flags & S_WACCEPT) == 0) FD_SET(lpc_socks[i].fd, &readmask); if (lpc_socks[i].flags & S_BLOCKED) FD_SET(lpc_socks[i].fd, &writemask); } } #endif } /* make_selectmasks() */ /* * Process I/O. */ INLINE void process_io() { int i; /* * check for new user connection. */ for (i = 0; i < 5; i++) { if (!external_port[i].port) continue; if (FD_ISSET(external_port[i].fd, &readmask)) { debug(connections, ("process_io: NEW_USER\n")); new_user_handler(i); } } /* * check for data pending on user connections. */ for (i = 0; i < max_users; i++) { if (!all_users[i] || (all_users[i]->iflags & (CLOSING | CMD_IN_BUF))) continue; if (all_users[i]->iflags & NET_DEAD) { remove_interactive(all_users[i]->ob, 0); continue; } if (FD_ISSET(all_users[i]->fd, &readmask)) { debug(connections, ("process_io: USER %d\n", i)); get_user_data(all_users[i]); if (!all_users[i]) continue; } if (FD_ISSET(all_users[i]->fd, &writemask)) flush_message(all_users[i]); } #if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL) /* * check for data pending on efun socket connections. */ for (i = 0; i < max_lpc_socks; i++) { if (lpc_socks[i].state != STATE_CLOSED) if (FD_ISSET(lpc_socks[i].fd, &readmask)) socket_read_select_handler(i); if (lpc_socks[i].state != STATE_CLOSED) if (FD_ISSET(lpc_socks[i].fd, &writemask)) socket_write_select_handler(i); } #endif /* * check for data pending from address server. */ if (addr_server_fd >= 0) { if (FD_ISSET(addr_server_fd, &readmask)) { debug(connections, ("process_io: IP_DAEMON\n")); hname_handler(); } } } /* * 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, x; object_t *master, *ob; svalue_t *ret; length = sizeof(addr); debug(connections, ("new_user_handler: accept on fd %d\n", external_port[which].fd)); new_socket_fd = accept(external_port[which].fd, (struct sockaddr *) & addr, (int *) &length); if (new_socket_fd < 0) { #ifdef EWOULDBLOCK if (socket_errno == EWOULDBLOCK) { debug(connections, ("new_user_handler: accept: Operation would block\n")); } else { #else if (1) { #endif socket_perror("new_user_handler: accept", 0); } return; } /* * according to Amylaar, 'accepted' sockets in Linux 0.99p6 don't * properly inherit the nonblocking property from the listening socket. * Marius, 19-Jun-2000: this happens on other platforms as well, so just * do it for everyone */ if (set_socket_nonblocking(new_socket_fd, 1) == -1) { socket_perror("new_user_handler: set_socket_nonblocking 1", 0); OS_socket_close(new_socket_fd); return; } /* find the first available slot */ for (i = 0; i < max_users; i++) if (!all_users[i]) break; if (i == max_users) { if (all_users) { all_users = RESIZE(all_users, max_users + 10, interactive_t *, TAG_USERS, "new_user_handler"); } else { all_users = CALLOCATE(10, interactive_t *, TAG_USERS, "new_user_handler"); } while (max_users < i + 10) all_users[max_users++] = 0; } set_command_giver(master_ob); master_ob->interactive = (interactive_t *) DXALLOC(sizeof(interactive_t), TAG_INTERACTIVE, "new_user_handler"); #ifndef NO_ADD_ACTION master_ob->interactive->default_err_message.s = 0; #endif master_ob->interactive->connection_type = external_port[which].kind; master_ob->flags |= O_ONCE_INTERACTIVE; /* * initialize new user interactive data structure. */ master_ob->interactive->ob = master_ob; #if defined(F_INPUT_TO) || defined(F_GET_CHAR) master_ob->interactive->input_to = 0; #endif master_ob->interactive->iflags = 0; master_ob->interactive->text[0] = '\0'; master_ob->interactive->text_end = 0; master_ob->interactive->text_start = 0; #if defined(F_INPUT_TO) || defined(F_GET_CHAR) master_ob->interactive->carryover = NULL; master_ob->interactive->num_carry = 0; #endif #ifndef NO_SNOOP master_ob->interactive->snooped_by = 0; #endif master_ob->interactive->last_time = current_time; #ifdef TRACE master_ob->interactive->trace_level = 0; master_ob->interactive->trace_prefix = 0; #endif #ifdef OLD_ED master_ob->interactive->ed_buffer = 0; #endif master_ob->interactive->message_producer = 0; master_ob->interactive->message_consumer = 0; master_ob->interactive->message_length = 0; master_ob->interactive->state = TS_DATA; master_ob->interactive->out_of_band = 0; for (x = 0; x < NSLC; x++) { master_ob->interactive->slc[x][0] = slc_default_flags[x]; master_ob->interactive->slc[x][1] = slc_default_chars[x]; } all_users[i] = master_ob->interactive; all_users[i]->fd = new_socket_fd; #ifdef F_QUERY_IP_PORT all_users[i]->local_port = external_port[which].port; #endif #ifdef F_NETWORK_STATS all_users[i]->external_port = which; #endif set_prompt("> "); memcpy((char *) &all_users[i]->addr, (char *) &addr, length); debug(connections, ("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. Save a pointer to the master ob incase it * changes during APPLY_CONNECT. We want to free the reference on * the right copy of the object. */ master = master_ob; add_ref(master_ob, "new_user"); push_number(external_port[which].port); ret = apply_master_ob(APPLY_CONNECT, 1); /* master_ob->interactive can be zero if the master object self destructed in the above (don't ask) */ set_command_giver(0); if (ret == 0 || ret == (svalue_t *)-1 || ret->type != T_OBJECT || !master_ob->interactive) { if (master_ob->interactive) remove_interactive(master_ob, 0); else free_object(master, "new_user"); 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. */ free_object(master, "new_user"); ob = ret->u.ob; #ifdef F_SET_HIDE if (ob->flags & O_HIDDEN) num_hidden_users++; #endif ob->interactive = master_ob->interactive; ob->interactive->ob = ob; ob->flags |= O_ONCE_INTERACTIVE; /* * assume the existance of write_prompt and process_input in user.c * until proven wrong (after trying to call them). */ ob->interactive->iflags |= (HAS_WRITE_PROMPT | HAS_PROCESS_INPUT); master_ob->flags &= ~O_ONCE_INTERACTIVE; master_ob->interactive = 0; add_ref(ob, "new_user"); set_command_giver(ob); if (addr_server_fd >= 0) { query_addr_name(ob); } if (external_port[which].kind == PORT_TELNET) { /* Ask permission to ask them for their terminal type */ add_binary_message(ob, telnet_do_ttype, sizeof(telnet_do_ttype)); /* Ask them for their window size */ add_binary_message(ob, telnet_do_naws, sizeof(telnet_do_naws)); } logon(ob); debug(connections, ("new_user_handler: end\n")); set_command_giver(0); } /* new_user_handler() */ /* * Return the first command of the next user in sequence that has a complete * command in their buffer. A command is defined to be a single character * when SINGLE_CHAR is set, or a newline terminated string otherwise. */ static char *get_user_command() { static int NextCmdGiver = 0; int i; interactive_t *ip; char *user_command = 0; /* find and return a user command */ for (i = 0; i < max_users; i++) { ip = all_users[NextCmdGiver++]; NextCmdGiver %= max_users; if (!ip || !ip->ob || ip->ob->flags & O_DESTRUCTED) continue; /* if we've got text to send, try to flush it, could lose the link here */ if (ip->message_length) { object_t *ob = ip->ob; flush_message(ip); if (!IP_VALID(ip, ob)) continue; } /* if there's a command in the buffer, pull it out! */ if (ip->iflags & CMD_IN_BUF) { NextCmdGiver++; NextCmdGiver %= max_users; user_command = first_cmd_in_buf(ip); break; } } /* no command found - return 0 */ if (!user_command) return 0; /* got a command - return it and set command_giver */ debug(connections, ("get_user_command: user_command = (%s)\n", user_command)); save_command_giver(ip->ob); #ifndef GET_CHAR_IS_BUFFERED if (ip->iflags & NOECHO) { #else if ((ip->iflags & NOECHO) && !(ip->iflags & SINGLE_CHAR)) { #endif /* must not enable echo before the user input is received */ add_binary_message(command_giver, telnet_no_echo, sizeof(telnet_no_echo)); ip->iflags &= ~NOECHO; } ip->last_time = current_time; return user_command; } /* get_user_command() */ static int escape_command P2(interactive_t *, ip, char *, user_command) { if (user_command[0] != '!') return 0; #ifdef OLD_ED if (ip->ed_buffer) return 1; #endif #if defined(F_INPUT_TO) || defined(F_GET_CHAR) if (ip->input_to && !(ip->iflags & NOESC)) return 1; #endif return 0; } static void process_input P2(interactive_t *, ip, char *, user_command) { svalue_t *ret; if (!(ip->iflags & HAS_PROCESS_INPUT)) { parse_command(user_command, command_giver); return; } /* * send a copy of user input back to user object to provide * support for things like command history and mud shell * programming languages. */ copy_and_push_string(user_command); ret = apply(APPLY_PROCESS_INPUT, command_giver, 1, ORIGIN_DRIVER); if (!IP_VALID(ip, command_giver)) return; if (!ret) { ip->iflags &= ~HAS_PROCESS_INPUT; parse_command(user_command, command_giver); return; } #ifndef NO_ADD_ACTION if (ret->type == T_STRING) { static char buf[MAX_TEXT]; strncpy(buf, ret->u.string, MAX_TEXT - 1); parse_command(buf, command_giver); } else { if (ret->type != T_NUMBER || !ret->u.number) parse_command(user_command, command_giver); } #endif } /* * This is the user command handler. This function is called when * a user command needs to be processed. * This function calls get_user_command() to get a user command. * One user command is processed per execution of this function. */ int process_user_command() { char *user_command; interactive_t *ip; /* * WARNING: get_user_command() sets command_giver via * save_command_giver(), but only when the return is non-zero! */ if (!(user_command = get_user_command())) return 0; ip = command_giver->interactive; current_interactive = command_giver; /* this is yuck phooey, sigh */ clear_notify(ip->ob); update_load_av(); debug(connections, ("process_user_command: command_giver = /%s\n", command_giver->name)); if (escape_command(ip, user_command)) { if (ip->iflags & SINGLE_CHAR) { /* only 1 char ... switch to line buffer mode */ ip->iflags |= WAS_SINGLE_CHAR; ip->iflags &= ~SINGLE_CHAR; #ifdef GET_CHAR_IS_BUFFERED ip->text_start = ip->text_end = *ip->text = 0; #endif set_linemode(ip); } else { if (ip->iflags & WAS_SINGLE_CHAR) { /* we now have a string ... switch back to char mode */ ip->iflags &= ~WAS_SINGLE_CHAR; ip->iflags |= SINGLE_CHAR; set_charmode(ip); if (!IP_VALID(ip, command_giver)) { goto exit; } } process_input(ip, user_command + 1); } goto exit; } #ifdef OLD_ED if (ip->ed_buffer) { ed_cmd(user_command); goto exit; } #endif #if defined(F_INPUT_TO) || defined(F_GET_CHAR) if (call_function_interactive(ip, user_command)) { goto exit; } #endif process_input(ip, user_command); exit: /* * Print a prompt if user is still here. */ if (IP_VALID(ip, command_giver)) print_prompt(ip); current_interactive = 0; restore_command_giver(); return 1; } #define HNAME_BUF_SIZE 200 /* * This is the hname input data handler. This function is called by the * master handler when data is pending on the hname socket (addr_server_fd). */ static void hname_handler() { static char hname_buf[HNAME_BUF_SIZE]; static int hname_buf_pos; int num_bytes; num_bytes = HNAME_BUF_SIZE - hname_buf_pos - 1; /* room for nul */ num_bytes = OS_socket_read(addr_server_fd, hname_buf + hname_buf_pos, num_bytes); if (num_bytes <= 0) { if (num_bytes == -1) { #ifdef EWOULDBLOCK if (socket_errno == EWOULDBLOCK) { debug(connections, ("hname_handler: read on fd %d: Operation would block.\n", addr_server_fd)); return; } #endif debug_message("hname_handler: read on fd %d\n", addr_server_fd); socket_perror("hname_handler: read", 0); } else { debug_message("hname_handler: closing address server connection.\n"); } OS_socket_close(addr_server_fd); addr_server_fd = -1; return; } hname_buf_pos += num_bytes; hname_buf[hname_buf_pos] = 0; debug(connections, ("hname_handler: address server replies: %s", hname_buf)); while (hname_buf_pos) { char *nl, *pp; /* if there's no newline, there's more data to come */ if (!(nl = strchr(hname_buf, '\n'))) break; *nl++ = 0; if ((pp = strchr(hname_buf, ' ')) != 0) { *pp++ = 0; got_addr_number(pp, hname_buf); if (isdigit(hname_buf[0])) { unsigned long laddr; if ((laddr = inet_addr(hname_buf)) != INADDR_NONE) { if (strcmp(pp, "0") != 0) add_ip_entry(laddr, pp); } } } hname_buf_pos -= (nl - hname_buf); if (hname_buf_pos) memmove(hname_buf, nl, hname_buf_pos + 1); /* be sure to get the nul */ } } /* * Remove an interactive user immediately. */ void remove_interactive P2(object_t *, ob, int, dested) { int idx; /* don't have to worry about this dangling, since this is the routine * that causes this to dangle elsewhere, and we are protected from * getting called recursively by CLOSING. safe_apply() should be * used here, since once we start this process we can't back out, * so jumping out with an error would be bad. */ interactive_t *ip = ob->interactive; if (!ip) return; if (ip->iflags & CLOSING) { if (!dested) debug_message("Double call to remove_interactive()\n"); return; } debug(connections, ("Closing connection from %s.\n", inet_ntoa(ip->addr.sin_addr))); flush_message(ip); ip->iflags |= CLOSING; #ifdef OLD_ED if (ip->ed_buffer) { save_ed_buffer(ob); } #else if (ob->flags & O_IN_EDIT) { object_save_ed_buffer(ob); ob->flags &= ~O_IN_EDIT; } #endif if (!dested) { /* * auto-notification of net death */ save_command_giver(ob); safe_apply(APPLY_NET_DEAD, ob, 0, ORIGIN_DRIVER); restore_command_giver(); } #ifndef NO_SNOOP if (ip->snooped_by) { ip->snooped_by->flags &= ~O_SNOOP; ip->snooped_by = 0; } #endif debug(connections, ("remove_interactive: closing fd %d\n", ip->fd)); if (OS_socket_close(ip->fd) == -1) { socket_perror("remove_interactive: close", 0); } #ifdef F_SET_HIDE if (ob->flags & O_HIDDEN) num_hidden_users--; #endif num_user--; clear_notify(ip->ob); #if defined(F_INPUT_TO) || defined(F_GET_CHAR) if (ip->input_to) { free_object(ip->input_to->ob, "remove_interactive"); free_sentence(ip->input_to); if (ip->num_carry > 0) free_some_svalues(ip->carryover, ip->num_carry); ip->carryover = NULL; ip->num_carry = 0; ip->input_to = 0; } #endif for (idx = 0; idx < max_users; idx++) if (all_users[idx] == ip) break; DEBUG_CHECK(idx == max_users, "remove_interactive: could not find and remove user!\n"); FREE(ip); ob->interactive = 0; all_users[idx] = 0; free_object(ob, "remove_interactive"); return; } /* remove_interactive() */ #if defined(F_INPUT_TO) || defined(F_GET_CHAR) static int call_function_interactive P2(interactive_t *, i, char *, str) { object_t *ob; funptr_t *funp; char *function; svalue_t *args; sentence_t *sent; int num_arg; #ifdef GET_CHAR_IS_BUFFERED int was_single = 0; int was_noecho = 0; #endif i->iflags &= ~NOESC; if (!(sent = i->input_to)) return (0); /* * Special feature: input_to() has been called to setup a call to a * function. */ if (sent->ob->flags & O_DESTRUCTED) { /* Sorry, the object has selfdestructed ! */ free_object(sent->ob, "call_function_interactive"); free_sentence(sent); i->input_to = 0; if (i->num_carry) free_some_svalues(i->carryover, i->num_carry); i->carryover = NULL; i->num_carry = 0; 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 */ STACK_INC; if (sent->flags & V_FUNCTION) { function = 0; sp->type = T_FUNCTION; sp->u.fp = funp = sent->function.f; funp->hdr.ref++; } else { sp->type = T_STRING; sp->subtype = STRING_SHARED; sp->u.string = function = sent->function.s; ref_string(function); } ob = sent->ob; free_sentence(sent); /* * If we have args, we have to copy them, so the svalues on the * interactive struct can be FREEd */ num_arg = i->num_carry; if (num_arg) { args = i->carryover; i->num_carry = 0; i->carryover = NULL; } else args = NULL; i->input_to = 0; if (i->iflags & SINGLE_CHAR) { /* * clear single character mode */ i->iflags &= ~SINGLE_CHAR; #ifndef GET_CHAR_IS_BUFFERED set_linemode(i); #else was_single = 1; if (i->iflags & NOECHO) { was_noecho = 1; i->iflags &= ~NOECHO; } #endif } copy_and_push_string(str); /* * If we have args, we have to push them onto the stack in the order they * were in when we got them. They will be popped off by the called * function. */ if (args) { transfer_push_some_svalues(args, num_arg); FREE(args); } /* current_object no longer set */ if (function) { if (function[0] == APPLY___INIT_SPECIAL_CHAR) error("Illegal function name.\n"); (void) apply(function, ob, num_arg + 1, ORIGIN_INTERNAL); } else call_function_pointer(funp, num_arg + 1); pop_stack(); /* remove `function' from stack */ #ifdef GET_CHAR_IS_BUFFERED if (IP_VALID(i, ob)) { if (was_single && !(i->iflags & SINGLE_CHAR)) { i->text_start = i->text_end = 0; i->text[0] = '\0'; i->iflags &= ~CMD_IN_BUF; set_linemode(i); } if (was_noecho && !(i->iflags & NOECHO)) add_binary_message(i->ob, telnet_no_echo, sizeof(telnet_no_echo)); } #endif return (1); } /* call_function_interactive() */ int set_call 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_binary_message(ob, telnet_yes_echo, sizeof(telnet_yes_echo)); if (flags & I_SINGLE_CHAR) set_charmode(ob->interactive); return (1); } /* set_call() */ #endif void set_prompt 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 P1(interactive_t*, ip) { object_t *ob = ip->ob; #if defined(F_INPUT_TO) || defined(F_GET_CHAR) if (ip->input_to == 0) { #endif /* give user object a chance to write its own prompt */ if (!(ip->iflags & HAS_WRITE_PROMPT)) tell_object(ip->ob, ip->prompt, strlen(ip->prompt)); #ifdef OLD_ED else if (ip->ed_buffer) tell_object(ip->ob, ip->prompt, strlen(ip->prompt)); #endif else if (!apply(APPLY_WRITE_PROMPT, ip->ob, 0, ORIGIN_DRIVER)) { if (!IP_VALID(ip, ob)) return; ip->iflags &= ~HAS_WRITE_PROMPT; tell_object(ip->ob, ip->prompt, strlen(ip->prompt)); } #if defined(F_INPUT_TO) || defined(F_GET_CHAR) } #endif if (!IP_VALID(ip, ob)) return; /* * Put the IAC GA thing in here... Moved from before writing the prompt; * vt src says it's a terminator. Should it be inside the no-input_to * case? We'll see, I guess. */ if ((ip->iflags & USING_TELNET) && !(ip->iflags & SUPPRESS_GA)) add_binary_message(command_giver, telnet_ga, sizeof(telnet_ga)); if (!IP_VALID(ip, ob)) return; } /* print_prompt() */ /* * Let object 'me' snoop object 'you'. If 'you' is 0, then turn off * snooping. * * This routine is almost identical to the old set_snoop. The main * difference is that the routine writes nothing to user directly, * all such communication is taken care of by the mudlib. It communicates * with master.c in order to find out if the operation is permissble or * not. The old routine let everyone snoop anyone. This routine also returns * 0 or 1 depending on success. */ #ifndef NO_SNOOP int new_set_snoop P2(object_t *, by, object_t *, victim) { interactive_t *ip; object_t *tmp; if (by->flags & O_DESTRUCTED) return 0; if (victim && (victim->flags & O_DESTRUCTED)) return 0; if (victim) { if (!victim->interactive) error("Second argument of snoop() is not interactive!\n"); ip = victim->interactive; } else { /* * Stop snoop. */ if (by->flags & O_SNOOP) { int i; for (i = 0; i < max_users; i++) { if (all_users[i] && all_users[i]->snooped_by == by) all_users[i]->snooped_by = 0; } by->flags &= ~O_SNOOP; } return 1; } /* * Protect against snooping loops. */ tmp = by; while (tmp) { if (tmp == victim) return 0; /* the person snooping us, if any */ tmp = (tmp->interactive ? tmp->interactive->snooped_by : 0); } /* * Terminate previous snoop, if any. */ if (by->flags & O_SNOOP) { int i; for (i = 0; i < max_users; i++) { if (all_users[i] && all_users[i]->snooped_by == by) all_users[i]->snooped_by = 0; } } if (ip->snooped_by) ip->snooped_by->flags &= ~O_SNOOP; by->flags |= O_SNOOP; ip->snooped_by = by; return 1; } /* set_new_snoop() */ #endif static void query_addr_name 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(connections, ("query_addr_name: sent address server %s\n", dbuf)); if (OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int)) == -1) { switch (socket_errno) { case EBADF: debug_message("Address server has closed connection.\n"); addr_server_fd = -1; break; default: socket_perror("query_addr_name: write", 0); break; } } } /* query_addr_name() */ #define IPSIZE 200 typedef struct { char *name; svalue_t call_back; object_t *ob_to_call; } ipnumberentry_t; static ipnumberentry_t ipnumbertable[IPSIZE]; /* * Does a call back on the current_object with the function call_back. */ int query_addr_number P2(char *, name, svalue_t *, 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)))) { share_and_push_string(name); push_undefined(); if (call_back->type == T_STRING) apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL); else call_function_pointer(call_back->u.fp, 2); return 0; } strcpy(dbuf, name); msglen = sizeof(int) + strlen(name) +1; msgtype = DATALEN; memcpy(buf, (char *) &msgtype, sizeof(msgtype)); memcpy(&buf[sizeof(int)], (char *) &msglen, sizeof(msglen)); msgtype = (name[0] >= '0' && name[0] <= '9') ? NAMEBYIP : IPBYNAME; memcpy(&buf[sizeof(int) + sizeof(int)], (char *) &msgtype, sizeof(msgtype)); debug(connections, ("query_addr_number: sent address server %s\n", dbuf)); if (OS_socket_write(addr_server_fd, buf, msglen + sizeof(int) + sizeof(int)) == -1) { switch (socket_errno) { case EBADF: debug_message("Address server has closed connection.\n"); addr_server_fd = -1; break; default: socket_perror("query_addr_name: write", 0); break; } share_and_push_string(name); push_undefined(); if (call_back->type == T_STRING) apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL); else call_function_pointer(call_back->u.fp, 2); return 0; } else { int i; /* We put ourselves into the pending name lookup entry table */ /* Find the first free entry */ for (i = 0; i < IPSIZE && ipnumbertable[i].name; i++) ; if (i == IPSIZE) { /* We need to error... */ share_and_push_string(name); push_undefined(); if (call_back->type == T_STRING) apply(call_back->u.string, current_object, 2, ORIGIN_INTERNAL); else call_function_pointer(call_back->u.fp, 2); return 0; } /* Create our entry... */ ipnumbertable[i].name = make_shared_string(name); assign_svalue_no_free(&ipnumbertable[i].call_back, call_back); ipnumbertable[i].ob_to_call = current_object; add_ref(current_object, "query_addr_number: "); return i + 1; } } /* query_addr_number() */ static void got_addr_number 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_svalue(&ipnumbertable[i].call_back, "got_addr_number"); free_string(ipnumbertable[i].name); free_object(ipnumbertable[i].ob_to_call, "got_addr_number: "); ipnumbertable[i].name = NULL; } for (i = 0; i < IPSIZE; i++) { if (ipnumbertable[i].name && strcmp(name, ipnumbertable[i].name)== 0) { /* Found one, do the call back... */ theName = ipnumbertable[i].name; theNumber = number; if (uisdigit(theName[0])) { char *tmp; tmp = theName; theName = theNumber; theNumber = tmp; } if (strcmp(theName, "0")) { share_and_push_string(theName); } else { push_undefined(); } if (strcmp(theNumber, "0")) { share_and_push_string(theNumber); } else { push_undefined(); } push_number(i + 1); if (ipnumbertable[i].call_back.type == T_STRING) safe_apply(ipnumbertable[i].call_back.u.string, ipnumbertable[i].ob_to_call, 3, ORIGIN_INTERNAL); else safe_call_function_pointer(ipnumbertable[i].call_back.u.fp, 3); free_svalue(&ipnumbertable[i].call_back, "got_addr_number"); free_string(ipnumbertable[i].name); free_object(ipnumbertable[i].ob_to_call, "got_addr_number: "); ipnumbertable[i].name = NULL; } } } /* got_addr_number() */ #undef IPSIZE #define IPSIZE 200 typedef struct { 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 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 0; 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() */ #ifndef NO_SNOOP object_t *query_snoop P1(object_t *, ob) { if (!ob->interactive) return 0; return ob->interactive->snooped_by; } /* query_snoop() */ object_t *query_snooping P1(object_t *, ob) { int i; if (!(ob->flags & O_SNOOP)) return 0; for (i = 0; i < max_users; i++) { if (all_users[i] && all_users[i]->snooped_by == ob) return all_users[i]->ob; } fatal("couldn't find snoop target.\n"); return 0; } /* query_snooping() */ #endif int query_idle P1(object_t *, ob) { if (!ob->interactive) error("query_idle() of non-interactive object.\n"); return (current_time - ob->interactive->last_time); } /* query_idle() */ #ifdef F_EXEC int replace_interactive 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"); } #ifdef F_SET_HIDE if ((ob->flags & O_HIDDEN) != (obfrom->flags & O_HIDDEN)) { if (ob->flags & O_HIDDEN) { num_hidden_users++; } else { num_hidden_users--; } } #endif ob->interactive = obfrom->interactive; /* * assume the existance of write_prompt and process_input in user.c until * proven wrong (after trying to call them). */ ob->interactive->iflags |= (HAS_WRITE_PROMPT | HAS_PROCESS_INPUT); obfrom->interactive = 0; ob->interactive->ob = ob; ob->flags |= O_ONCE_INTERACTIVE; obfrom->flags &= ~O_ONCE_INTERACTIVE; add_ref(ob, "exec"); free_object(obfrom, "exec"); if (obfrom == command_giver) { set_command_giver(ob); } return (1); } /* replace_interactive() */ #endif 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; DEBUG_CHECK(l < 0, "Negative length passed to outbuf_extend.\n"); l = (l > USHRT_MAX ? USHRT_MAX : l); 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 = 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_extend"); 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 ((limit = outbuf_extend(outbuf, l)) > 0) { strncpy(outbuf->buffer + outbuf->real_size, str, limit); outbuf->real_size += (l > limit ? limit : l); *(outbuf->buffer + outbuf->real_size) = 0; } } void outbuf_addchar P2(outbuffer_t *, outbuf, char, c) { if (outbuf && (outbuf_extend(outbuf, 1)) > 0) { *(outbuf->buffer + outbuf->real_size++) = c; *(outbuf->buffer + outbuf->real_size) = 0; } } void outbuf_addv P2V(outbuffer_t *, outbuf, char *, format) { char buf[LARGEST_PRINTABLE_STRING + 1]; 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) { STACK_INC; sp->type = T_STRING; if (outbuf && outbuf->buffer) { outbuf->buffer = extend_string(outbuf->buffer, outbuf->real_size); sp->subtype = STRING_MALLOC; sp->u.string = outbuf->buffer; } else { sp->subtype = STRING_CONSTANT; sp->u.string = ""; } }