#include <sys/time.h> #ifdef AMIGA #include <stdarg.h> #include <ioctl.h> #else #include <sys/ioctl.h> #endif /* AMIGA */ #define TELOPTS #if defined (atarist) || defined (AMIGA) #include "telnet.h" #else #include <arpa/telnet.h> #include <netdb.h> #endif #include <stdio.h> #include <errno.h> #include <ctype.h> #include <signal.h> #include "config.h" #ifdef _AIX #include <sys/select.h> #endif #include "lint.h" #if defined(USE_FCNTL_O_NDELAY) || defined(USE_FCNTL_FNDELAY) #include <fcntl.h> #endif #include "interpret.h" #include "comm.h" #include "object.h" #include "sent.h" #include "patchlevel.h" #include "wiz_list.h" #ifdef MUDWHO #include "mudwho.h" #endif #ifdef AMIGA #include "hosts/amiga/ixfile.h" #endif #if !defined(AMIGA) || !defined(_DCC) #include SOCKET_INC #endif void telnet_neg PROT((char *, char *)); char *query_ip_number PROT((struct object *)); extern int d_flag; extern int current_time; extern int trace_level; char * first_cmd_in_buf PROT((struct interactive *)); void next_cmd_in_buf PROT((struct interactive *)); char * skip_eols PROT((struct interactive *, char *)); void remove_flush_entry PROT((struct interactive *ip)); void remove_interactive(); struct interactive *all_players[MAX_PLAYERS]; extern int errno; void new_player(); void flush_all_player_mess(); fd_set readfds; int nfds = 0; int num_player; #ifndef NO_IP_DEMON FILE *f_ip_demon = NULL, *f_ip_demon_wr; #endif #ifdef ACCESS_RESTRICTED extern void *allow_host_access (); extern void release_host_access (); #endif #ifdef QUOTE_IAC static char sending_telnet_command = 0; #define SEND_TELNET_COMMAND(TEXT)\ sending_telnet_command = 1;\ TEXT\ sending_telnet_command = 0; #else #define SEND_TELNET_COMMAND(TEXT) TEXT #endif /* QUOTE_IAC */ static struct object *first_player_for_flush=(struct object *)NULL; /* * Interprocess communication interface to the backend. */ extern int port_number; #ifdef UDP_SEND static SOCKET_T udp_send = -1; #endif #ifdef CATCH_UDP_PORT extern int udp_port; static SOCKET_T udp_s; #endif #ifdef COMM_STAT int add_message_calls=0; int inet_packets=0; int inet_volume=0; #endif static SOCKET_T s; #ifndef MSDOS void set_socket_nonblocking(new_socket) SOCKET_T new_socket; { int tmp; tmp = 1; #ifdef USE_IOCTL_FIONBIO if (socket_ioctl(new_socket, FIONBIO, &tmp) == -1) { perror("ioctl socket FIONBIO"); abort(); } #else /* !USE_IOCTL_FIONBIO */ #ifdef USE_FCNTL_O_NDELAY if (fcntl(new_socket, F_SETFL, O_NDELAY) == -1) { #else if (fcntl(new_socket, F_SETFL, FNDELAY) == -1) { #endif perror("fcntl socket FNDELAY"); abort(); } #endif /* !USE_IOCTL_FIONBIO */ } #endif /* MSDOS */ void prepare_ipc() { #ifndef MSDOS struct sockaddr_in my_sin; struct hostent *hp; int tmp; char host_name[100]; #ifdef MUDWHO char buff[100]; #endif if (gethostname(host_name, sizeof host_name) == -1) { perror("gethostname"); fatal("Error in gethostname()\n"); } hp = gethostbyname(host_name); if (hp == 0) { (void)fprintf(stderr, "gethostbyname: unknown host.\n"); exit(1); } memset((char *)&my_sin, '\0', sizeof my_sin); memcpy((char *)&my_sin.sin_addr, hp->h_addr, hp->h_length); my_sin.sin_port = htons((u_short)port_number); my_sin.sin_family = hp->h_addrtype; my_sin.sin_addr.s_addr = INADDR_ANY; s = socket(hp->h_addrtype, SOCK_STREAM, 0); if ((int)s == -1) { perror("socket"); abort(); } tmp = 1; if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp)) < 0) { perror ("setsockopt"); exit (1); } if (bind(s, (struct sockaddr *)&my_sin, sizeof my_sin) == -1) { if (errno == EADDRINUSE) { fprintf(stderr, "Socket already bound!\n"); debug_message("Socket already bound!\n"); exit(errno); } else { perror("bind"); abort(); } } if (listen(s, 5) == -1) { perror("listen"); abort(); } set_socket_nonblocking(s); signal(SIGPIPE, SIG_IGN); #ifdef CATCH_UDP_PORT if (udp_port != -1) { my_sin.sin_port = htons((u_short)udp_port); debug_message("UDP recv-socket on port: %d\n", udp_port); udp_s = socket(hp->h_addrtype, SOCK_DGRAM, 0); if (udp_s == -1) { perror("udp_socket"); abort(); } tmp = 1; if (setsockopt (udp_s, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp)) < 0) { perror ("setsockopt"); exit (1); } if (bind(udp_s, (struct sockaddr *)&my_sin, sizeof my_sin) == -1) { if (errno == EADDRINUSE) { fprintf(stderr, "UDP Socket already bound!\n"); debug_message("UDP Socket already bound!\n"); close(udp_s); udp_port = -1; } else { perror("udp-bind"); abort(); } } } if (udp_port != -1) set_socket_nonblocking(udp_s); #endif /* CATCH_UDP_PORT */ #ifdef MUDWHO sprintf(buff, "3.02%s", PATCH_LEVEL); rwhocli_setup(MUDWHO_SERVER,MUDWHO_PASSWORD,MUDWHO_NAME,buff); #endif #else /* MSDOS */ c_listen(); #endif /* MSDOS */ } /* * This one is called when shutting down the MUD. */ void ipc_remove() { (void)printf("Shutting down ipc...\n"); #ifndef MSDOS socket_close(s); #ifdef MUDWHO rwhocli_shutdown(); #endif #ifdef UDP_SEND if (udp_send >= 0) socket_close(udp_send); #endif #ifdef CATCH_UDP_PORT if (udp_s >= 0) socket_close(udp_s); #endif #else c_shutdown(); #endif } /* to allow messages to go to a interactive player who's object is currently * destructed, there is the special format string destruct_add_message_format. * It has to be initialized as array of char, because a string could be made * shared. */ char destruct_add_message_format[] = { '%', 's', '\0' }; /* * Send a message to a player. If that player is shadowed, special * care has to be taken. */ #if defined(AMIGA) void add_message(char *fmt, ...) #else /*VARARGS1*/ void add_message(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) char *fmt; int a1, a2, a3, a4, a5, a6, a7, a8, a9; #endif { char buff[10001]; /* Kludgy! Hope this is enough ! */ char buff2[MAX_SOCKET_PACKET_SIZE+1]; struct interactive *ip; int n, chunk, length; int from, to; int min_length; int old_message_length; struct object *snooper; char *rest; int retries; #if defined(AMIGA) va_list va; #endif #if defined(AMIGA) va_start(va, fmt); #endif min_length = DESIRED_SOCKET_PACKET_SIZE; if (command_giver == 0 || ( command_giver->flags & O_DESTRUCTED && fmt != destruct_add_message_format && fmt != MESSAGE_FLUSH ) || command_giver->interactive == 0 || (command_giver->interactive->do_close && fmt != MESSAGE_FLUSH) ) { putchar(']'); if ( fmt != MESSAGE_FLUSH ) { #if defined(AMIGA) vprintf(fmt, va); #else printf(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9); #endif } fflush(stdout); #if defined(AMIGA) va_end(va); #endif return; } ip = command_giver->interactive; old_message_length = ip->message_length; rest = 0; if ( fmt == MESSAGE_FLUSH ) { min_length = 1; strncpy ( buff+1, ip->message_buf, length=old_message_length ); (buff+1)[length] = '\0'; } else { #ifdef COMM_STAT add_message_calls++; /* We want to know how many packets the old version would have sent */ #endif if (fmt[0] == '%' && fmt[1] == 's' && !fmt[2]) { #if defined(AMIGA) fmt = va_arg(va, char *); va_end(va); #else fmt = (char *)a1; #endif buff[(sizeof buff) - 1] = '\0'; strncpy( buff+1+old_message_length, fmt, (sizeof buff) -1 -old_message_length ); if (buff[(sizeof buff) - 1]) { buff[(sizeof buff) - 1] = '\0'; rest = fmt + (sizeof buff) -2 -old_message_length; } length = old_message_length + strlen(buff+1+old_message_length); } else { #if defined(AMIGA) (void)vsprintf(buff+1+old_message_length,fmt,va); va_end(va); #else (void)sprintf( buff+1+old_message_length, fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9 ); #endif length = old_message_length + strlen(buff+1+old_message_length); /* * Always check that your arrays are big enough ! :-) */ if (length > (sizeof buff) - 1) fatal("To long message!\n"); } if ( #ifdef QUOTE_IAC !sending_telnet_command && #endif shadow_catch_message(command_giver, buff+1+old_message_length)) return; if (snooper = ip->snoop_by) { buff[old_message_length] = '%'; if (snooper->interactive) { struct object *save; save = command_giver; command_giver = snooper; add_message("%s", buff+old_message_length); command_giver = save; } else { trace_level |= ip->trace_level; tell_npc(snooper, buff+old_message_length); } } if ( length >= min_length ) { strncpy ( buff+1, ip->message_buf, old_message_length ); } else { strcpy( ip->message_buf+old_message_length, buff+1+old_message_length ); } } #ifdef DEBUG if (d_flag > 1) if ( length >= min_length ) debug_message("[%s(%d)]: %s", command_giver->name, length, buff+1); else debug_message("[%s(%d)]: %s(cntd.)\n", command_giver->name, length, ip->message_buf); #endif /* * Insert CR after all NL. */ to = 0; #ifdef PORTALS if (ip->out_portal) { buff2[0] = ']'; to = 1; } #endif for (from = 0; length-from >= min_length; to = 0 ) { char c; for ( ; to < (sizeof buff2)-1 && (c = (buff+1)[from] );) { if (c == '\n') buff2[to++] = '\r'; #if 0 if (isprint(c) || isspace(c)) buff2[to++] = c; #else #ifdef QUOTE_IAC if ((unsigned char)c == IAC && !sending_telnet_command) buff2[to++] = c; #endif buff2[to++] = c; #endif from++; } if ( to == sizeof(buff2) ) { /* the last character is doubled by translation, so it doesn't * fit in the packet */ to -= 2; from--; } chunk = to; /* * We split up the message into something smaller than the max size. */ for (retries = 6;;) { if ((n = socket_write(ip->socket, buff2, chunk)) != -1) break; switch (errno) { case EINTR: if (--retries) continue; fprintf(stderr, "comm1: write EINTR. Message discarded.\n"); if (old_message_length) remove_flush_entry(ip); return; case EWOULDBLOCK: fprintf(stderr, "comm1: write EWOULDBLOCK. Message discarded.\n"); if (old_message_length) remove_flush_entry(ip); /* ip->do_close = 1; -- LA */ return; case EMSGSIZE: fprintf(stderr, "comm1: write EMSGSIZE.\n"); return; case EINVAL: fprintf(stderr, "comm1: write EINVAL.\n"); break; case ENETUNREACH: fprintf(stderr, "comm1: write ENETUNREACH.\n"); break; case EHOSTUNREACH: fprintf(stderr, "comm1: write EHOSTUNREACH.\n"); break; case EPIPE: fprintf(stderr, "comm1: write EPIPE detected\n"); break; default: fprintf(stderr, "comm1: write: unknown errno %d\n", errno); perror("write"); } if (old_message_length) remove_flush_entry(ip); ip->do_close = 1; return; } #ifdef COMM_STAT inet_packets++; inet_volume += n; #endif if (n != chunk) fprintf(stderr, "write socket: wrote %d, should be %d.\n", n, chunk); continue; } length -= from; ip->message_length=length; if (from) strncpy( ip->message_buf, buff+1+from, length ); if ( length && !old_message_length ) { /* buffer became 'dirty' */ if ( ip->next_player_for_flush = first_player_for_flush ) { first_player_for_flush->interactive->previous_player_for_flush = command_giver; } ip->previous_player_for_flush = 0; first_player_for_flush = command_giver; } if ( !length && old_message_length ) { /* buffer has become empty */ remove_flush_entry(ip); } if (rest) add_message("%s", rest); } void remove_flush_entry(ip) struct interactive *ip; { ip->message_length=0; if ( ip->previous_player_for_flush ) { ip->previous_player_for_flush->interactive->next_player_for_flush = ip->next_player_for_flush; } else { first_player_for_flush = ip->next_player_for_flush; } if ( ip->next_player_for_flush ) { ip->next_player_for_flush->interactive->previous_player_for_flush = ip->previous_player_for_flush; } } void flush_all_player_mess() { struct object *p,*np; struct object *save = command_giver; for ( p = first_player_for_flush; p; p=np) { np = p->interactive->next_player_for_flush; /* beware of side-effects when calling add_message the first time! */ command_giver = p; add_message(MESSAGE_FLUSH); } command_giver=save; } /* * 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. */ static int copy_chars(from, to, n) char *from, *to; int n; { int i; char *start = to; for (i=0; i<n; i++) { if (from[i] == '\r') continue; if (from[i] == '\n') { *to++ = ' '; *to++ = '\b'; *to++ = '\0'; continue; } *to++ = from[i]; } return to - start; } /* * Get a message from any player. For all players without a completed * cmd in their input buffer, read their socket. Then, return the first * cmd of the next player in sequence that has a complete cmd in their buffer. * CmdsGiven is used to allow people in ED to send more cmds (if they have * them queued up) than normal players. If we get a heartbeat, still read * all sockets; if the next cmd giver is -1, we have already cycled and * can go back to do the heart beat. */ #define StartCmdGiver (MAX_PLAYERS-1) /* the one after heartbeat */ #define IncCmdGiver NextCmdGiver = (NextCmdGiver < 0? StartCmdGiver: \ NextCmdGiver - 1) int NextCmdGiver = StartCmdGiver; int CmdsGiven = 0; /* -1 is used to poll heart beat. */ int twait = 0; /* wait time for select() */ extern int time_to_call_heart_beat, comm_time_to_call_heart_beat; int get_message(buff, size) char *buff; int size; { int i, res; struct interactive * ip = 0; char *p; /* * Stay in this loop until we have a message from a player. */ while(1) { struct object *snooper; SOCKET_T new_socket; struct sockaddr_in addr; int length; struct timeval timeout; #ifdef CATCH_UDP_PORT char udp_buf[1024], *st; #endif /* First, try to get a new player... */ length = sizeof addr; new_socket = accept(s, (struct sockaddr *)&addr, &length); if ((int)new_socket != -1) new_player(new_socket, &addr, length); else if ((int)new_socket == -1 && errno != EWOULDBLOCK && errno != EINTR && errno != EAGAIN && errno != EBADF ) { perror("accept"); abort(); } #ifdef CATCH_UDP_PORT /* Then see if we got any udp messages */ if (udp_port != -1) { int cnt; cnt = recvfrom(udp_s, udp_buf, sizeof(udp_buf), 0, (struct sockaddr *)&addr, &length); if (cnt != -1) { command_giver = 0; /*HERP*/ trace_level = 0; udp_buf[sizeof(udp_buf) - 1] = 0; udp_buf[cnt] = 0; st = inet_ntoa(addr.sin_addr); push_string_malloced((st)); push_string_malloced((udp_buf)); apply_master_ob("receive_imp", 2); } } #endif nfds = 0; FD_ZERO(&readfds); for (i=0; i<MAX_PLAYERS; i++) { ip = all_players[i]; if (!ip) continue; if (ip->do_close) { ip->do_close = 0; remove_interactive(ip->ob); continue; } if (!first_cmd_in_buf(ip)) { FD_SET(ip->socket, &readfds); if (socket_number(ip->socket) >= nfds) nfds = socket_number(ip->socket)+1; #ifdef PORTALS if (ip->out_portal) { FD_SET(ip->portal_socket, &readfds); if (ip->portal_socket >= nfds) nfds = ip->portal_socket + 1; } #endif /* PORTALS */ } } #ifndef NO_IP_DEMON if (f_ip_demon != NULL) { FD_SET(fileno(f_ip_demon), &readfds); } #endif timeout.tv_sec = twait; /* avoid busy waiting when no buffered cmds */ timeout.tv_usec = 0; res = socket_select(nfds, &readfds, 0, 0, &timeout); #ifdef MSDOS if (timer_expired()) { twait = 0; goto return_next_command; } #endif if (res == -1) { twait = 0; if (errno == EINTR) /* if we got an alarm, finish the round */ goto return_next_command; perror("select"); return 0; } if (res) { /* waiting packets */ #ifndef NO_IP_DEMON static void add_ip_entry PROT((long, char *)); if (f_ip_demon != NULL && FD_ISSET(fileno(f_ip_demon), &readfds)) { char buf[200], *pp, *q; long laddr; if (fgets(buf, sizeof buf, f_ip_demon)) { /*printf("hname says: %s\n", buf);*/ laddr=inet_addr(buf); if (laddr != -1) { pp = strchr(buf, ' '); if (pp) { pp++; q = strchr(buf, '\n'); if (q) { *q = 0; add_ip_entry(laddr, pp); } } } } } #endif /* NO_IP_DEMON */ for (i=0; i<MAX_PLAYERS; i++) { /* read all pending sockets */ ip = all_players[i]; if (ip == 0) continue; if (FD_ISSET(ip->socket, &readfds)) { /* read this player */ int l; /* * Don't overfill their buffer. * Use a very conservative estimate on how much we can * read. */ l = (MAX_TEXT - ip->text_end - 1)/3; if (l < size) size = l; if ((l = socket_read(ip->socket, buff, size)) == -1) { if (errno == ENETUNREACH) { debug_message("Net unreachable detected.\n"); remove_interactive(ip->ob); continue; } if (errno == EHOSTUNREACH) { debug_message("Host unreachable detected.\n"); remove_interactive(ip->ob); continue; } if (errno == ETIMEDOUT) { debug_message("Connection timed out detected.\n"); remove_interactive(ip->ob); continue; } if (errno == ECONNRESET) { debug_message("Connection reset by peer detected.\n"); remove_interactive(ip->ob); continue; } if (errno == EWOULDBLOCK) { debug_message("read would block socket %d!\n", ip->socket); remove_interactive(ip->ob); continue; } if (errno == EMSGSIZE) { debug_message("read EMSGSIZE !\n"); continue; } perror("read"); debug_message("Unknown errno %d\n", errno); remove_interactive(ip->ob); continue; } #ifdef PORTALS /* * IF the data goes through a portal, send it, * but don't return any data. */ if (ip->out_portal) { if (ip->text_end) { /* pending text first */ socket_write( ip->portal_socket, ip->text, ip->text_end ); ip->text_end = 0; } if (l) socket_write(ip->portal_socket, buff, l); continue; } #endif /* PORTALS */ if (l == 0) { if (ip->closing) fatal("Tried to read from closing socket.\n"); remove_interactive(ip->ob); return 0; } buff[l] = '\0'; /* replace newlines by nulls and catenate to buffer */ ip->text_end += copy_chars(buff, ip->text + ip->text_end, l); #if 0 for (x=0; x<l; x++) { if (buff[x] == '\n' || buff[x] == '\r') buff[x] = '\0'; } memcpy(ip->text+ip->text_end, buff, l+1); ip->text_end += l; #endif /* 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'; } } } /* * we have read the sockets; now find and return a command */ return_next_command: twait = 0; ip = 0; for (i = 0; i < MAX_PLAYERS + 1; i++) { if (NextCmdGiver == -1) { /* we have cycled around all players */ CmdsGiven = 0; /* check heart beat */ IncCmdGiver; if (comm_time_to_call_heart_beat) { time_to_call_heart_beat = 1; /* twait stays 0! */ return 0; } } ip = all_players[NextCmdGiver]; if (ip && (p = first_cmd_in_buf(ip))) /* wont respond to partials */ break; CmdsGiven = 0; /* new player, no cmds issued */ IncCmdGiver; } if ((!ip)||!p) { /* no cmds found; loop and select (on timeout) again */ if (comm_time_to_call_heart_beat) { /* may as well do it now */ time_to_call_heart_beat = 1; /* no cmds, do heart beat */ NextCmdGiver = StartCmdGiver;/* do a complete poll next time */ CmdsGiven = 0; return(0); } /* no heart beat to do and no cmds pending - avoid busy wait on select */ twait = 1; continue; /* else await another cmd */ } /* * we have a player cmd - return it. If he is in ed, count his * cmds, else only allow 1 cmd. If he has only one partially * completed cmd left after * this, move it to the start of his * buffer; new stuff will be appended. */ command_giver = ip->ob; trace_level = ip->trace_level; telnet_neg(buff, p); next_cmd_in_buf(ip); /* move on buffer pointers */ /* if he is not in ed, dont let him issue another till the poll comes again */ if (ip->ed_buffer && CmdsGiven < ALLOWED_ED_CMDS) CmdsGiven++; else { IncCmdGiver; CmdsGiven = 0; } /* manage snooping - should the snooper see type ahead? Well, he doesn't here */ if ((snooper = ip->snoop_by) && !ip->noecho) { if (snooper->interactive) { command_giver = snooper; add_message("%% %s\n", buff); } else { char *snoop_message = alloca(strlen(buff) + 4); (void)sprintf(snoop_message, "%% %s\n", buff); tell_npc(snooper, snoop_message); } command_giver = ip->ob; } ip->last_time = current_time; return 1; } } /* * 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. */ char * first_cmd_in_buf(ip) struct interactive *ip; { char * p, *q; p = ip->text_start + ip->text; while ((p < (ip->text_end + ip->text)) && !*p) /* skip null input */ 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(0); } while ((p < (ip->text_end + ip->text)) && *p) /* find end of cmd */ p++; if (p < ip->text + ip->text_end) /* null terminated, was command */ 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(0); } /* * move pointers to next cmd, or clear buf. */ void next_cmd_in_buf(ip) struct interactive *ip; { char * p = ip->text + ip->text_start; while (*p && p < ip->text + ip->text_end) p++; /* skip past any nulls at the end */ while (!*p && p < ip->text + ip->text_end) p++; if (p < ip->text + ip->text_end) ip->text_start = p - ip->text; else { ip->text_start = ip->text_end = 0; ip->text[0] = '\0'; } } /* * Remove an interactive player immediately. */ void remove_interactive(ob) struct object *ob; { extern int malloc_privilege; struct object *save = command_giver; int i; static char iac_string[] = { IAC, '\0' }; static char ip_string[] = { IP, '\0' }; struct interactive *interactive; interactive = ob->interactive; for (i=0; i<MAX_PLAYERS; i++) { if (all_players[i] != interactive) continue; if (interactive->closing) fatal("Double call to remove_interactive()\n"); interactive->closing = 1; if ( !(ob->flags & O_DESTRUCTED) ) { push_object(ob); malloc_privilege = MALLOC_MASTER; apply_master_ob("disconnect", 1); /* master might have used exec() */ ob = interactive->ob; } interactive->catch_tell_activ = 0; if (interactive->snoop_by) { if (interactive->snoop_by->interactive) { interactive->snoop_by->interactive->snoop_on = 0; } else { free_object(interactive->snoop_by, "remove_interactive"); } interactive->snoop_by = 0; } if (interactive->snoop_on) { interactive->snoop_on->snoop_by = 0; interactive->snoop_on = 0; } command_giver = ob; trace_level |= interactive->trace_level; add_message(destruct_add_message_format, "Closing down.\n"); if (interactive->ed_buffer) { extern void save_ed_buffer(); malloc_privilege = MALLOC_MASTER; save_ed_buffer(); ob = interactive->ob; } SEND_TELNET_COMMAND( add_message(destruct_add_message_format, iac_string); add_message(destruct_add_message_format, ip_string); ) add_message(MESSAGE_FLUSH); (void)shutdown(interactive->socket, 2); socket_close(interactive->socket); #ifdef ACCESS_RESTRICTED release_host_access (interactive->access_class); #endif num_player--; if (interactive->input_to) { free_input_to(interactive->input_to); } if (interactive->modify_command) { free_object(interactive->modify_command, "remove_interactive"); } free_svalue(&interactive->prompt); if (interactive->default_err_message) { free_string(interactive->default_err_message); interactive->default_err_message = 0; } xfree((char *)ob->interactive); ob->interactive = 0; all_players[i] = 0; free_object(ob, "remove_interactive"); command_giver = check_object(save); return; } (void)fprintf(stderr, "Could not find and remove player %s\n", ob->name); abort(); } #ifndef MSDOS #ifndef ACCESS_RESTRICTED int allow_host_access(new_socket) SOCKET_T new_socket; { struct sockaddr_in apa; int len = sizeof apa; char * ipname; static int read_access_list = 0; static struct access_list { int addr_len; char * addr, *name, *comment; struct access_list * next; } * access_list; register struct access_list * ap; if(!read_access_list) { FILE * f = fopen("ACCESS.DENY", "r"); char buf[1024], ipn[50], hname[100], comment[1024], *p1, *p2; struct access_list * na; struct hostent * hent; read_access_list = 1; if(f) { while(fgets(buf, sizeof buf - 1, f)) { if(*buf != '#') { ipn[0] = hname[0] = comment[0] = 0; if(p1 = strchr(buf, ':')) *p1 = 0; if(buf[0] && buf[0] != '\n') strncpy(ipn, buf, sizeof ipn - 1); if((p2 = p1) && *++p2) { if(p1 = strchr(p2, ':')) *p1 = 0; if(p2[0] && p2[0] != '\n') strcpy(hname, p2); if(p1 && p1[1] && p1[1] != '\n') strcpy(comment, p1+1); } if(!(na = (struct access_list *)permanent_xalloc(sizeof na[0]))) { fatal("Out of mem.\n"); } na->addr = na->name = na->comment = 0; na->next = 0; if (*ipn) { if ( !(na->addr = permanent_xalloc(strlen(ipn) + 1) )) fatal("Out of mem.\n"); strcpy(na->addr, ipn); } if (*hname) { if ( !(na->name = permanent_xalloc(strlen(hname)+1)) ) fatal("Out of mem.\n"); strcpy(na->name, hname); } if (*comment) { if (!(na->comment=permanent_xalloc(strlen(comment)+1))) fatal("Out of mem.\n"); strcpy(na->comment, comment); } if((!(int)*ipn) && ((!*hname) || (!(hent = gethostbyname(hname))) || (!(na->addr = permanent_xalloc(hent->h_length+1)))|| !strcpy(na->addr, inet_ntoa(*(struct in_addr *)hent->h_addr)))) { if(na->name) pfree(na->name); if(na->comment) pfree(na->comment); pfree((char *)na); continue; } if(!(na->addr_len = strlen(na->addr))) continue; /* printf("disabling: %s:%s:%s\n", na->addr, na->name?na->name:"no name", na->comment?na->comment:"no comment"); */ na->next = access_list; access_list = na; } } fclose(f); } } if (!access_list) return 0; if(getpeername(new_socket, (struct sockaddr *)&apa, &len) == -1) { socket_close(new_socket); perror("getpeername"); return -1; } ipname = inet_ntoa(apa.sin_addr); for(ap = access_list; ap; ap = ap->next) if(!strncmp(ipname, ap->addr, ap->addr_len)){ if(ap->comment) (void) socket_write(new_socket, ap->comment, strlen(ap->comment)); printf("Stopping: %s:%s\n", ap->addr, ap->name?ap->name:"no name"); socket_close(new_socket); return -1; } return 0; } #endif /* not ACCESS_RESTRICTED */ #endif /* * get the I'th player object from the interactive list, i starts at 0 * and can go to num_player - 1. For users(), etc. */ struct object * get_interactive_object(i) int i; { int n; if (i >= num_player) /* love them ASSERTS() :-) */ fatal("Get interactive (%d) with only %d players!", i, num_player); for (n = 0; n < MAX_PLAYERS; n++) if (all_players[n] /*&& !(all_players[n]->ob->flags & O_DESTRUCTED)*/ ) if (!(i--)) return(all_players[n]->ob); fatal("Get interactive: player %d not found! (num_players = %d)", i, num_player); return 0; /* Just to satisfy some compiler warnings */ } struct vector *users() { struct object *ob; int n, num; struct vector *ret; struct interactive **user; struct svalue *svp; num = 0; user = all_players; for (n = MAX_PLAYERS + 1; --n; user++) { if (*user && !((*user)->ob->flags & O_DESTRUCTED)) num++; } ret = allocate_array(num); svp = ret->item; user = all_players; for (n = MAX_PLAYERS + 1; --n; user++) { if (*user && !((ob = (*user)->ob)->flags & O_DESTRUCTED)) { svp->type = T_OBJECT; svp->u.ob = ob; add_ref(ob, "users"); svp++; } } return ret; } void new_player(new_socket, addr, len) SOCKET_T new_socket; struct sockaddr_in *addr; int len; { int i; char *p; #ifndef MSDOS #ifdef ACCESS_RESTRICTED void *class; set_socket_nonblocking(new_socket); if (!(class = allow_host_access (new_socket, new_socket))) return; #else if(allow_host_access(new_socket)) return; #endif /* ACCESS_RESTRICTED */ #endif /* MSDOS */ if (d_flag > 1) debug_message("New player at socket %d.\n", new_socket); for (i=0; i<MAX_PLAYERS; i++) { extern struct object *master_ob; struct object *ob; struct svalue *ret; struct interactive *new_interactive; if (all_players[i] != 0) continue; assert_master_ob_loaded(); if (master_ob->interactive) { p = "Cannot accept connections. Come back later.\r\n"; socket_write(new_socket, p, strlen(p)); socket_close(new_socket); return; } command_giver = master_ob; trace_level = 0; master_ob->interactive = new_interactive = (struct interactive *)xalloc(sizeof (struct interactive)); master_ob->flags |= O_ONCE_INTERACTIVE; new_interactive->ob = master_ob; new_interactive->input_to = 0; new_interactive->prompt.type = T_STRING; new_interactive->prompt.x.string_type = STRING_CONSTANT; new_interactive->prompt.u.string = "> "; new_interactive->modify_command = 0; new_interactive->closing = 0; new_interactive->do_close = 0; new_interactive->noecho = 0; new_interactive->catch_tell_activ = 1; new_interactive->text_end = 0; new_interactive->text_start = 0; new_interactive->snoop_on = 0; new_interactive->snoop_by = 0; new_interactive->last_time = current_time; new_interactive->default_err_message = 0; new_interactive->trace_level = 0; new_interactive->trace_prefix = 0; new_interactive->ed_buffer = 0; new_interactive->message_length=0; #ifdef PORTAL new_interactive->from_portal = 0; new_interactive->portal_socket = 0; new_interactive->out_portal = 0; #endif /* PORTAL */ #ifdef MUDWHO new_interactive->login_time = current_time; #endif new_interactive->text[0] = '\0'; all_players[i] = master_ob->interactive; all_players[i]->socket = new_socket; #if 1 memcpy((char *)&all_players[i]->addr, (char *)addr, len); #else getpeername(new_socket, (struct sockaddr *)&all_players[i]->addr, &len); #endif #ifdef ACCESS_RESTRICTED all_players[i]->access_class = class; #endif num_player++; /* * The player object has one extra reference. * It is asserted that the master_ob is loaded. */ add_ref(master_ob, "new_player"); ret = apply_master_ob("connect", 0); if (ret == 0 || ret->type != T_OBJECT) { remove_interactive(master_ob); return; } /* * There was an object returned from connect(). Use this as the * player object. */ ob = ret->u.ob; ob->interactive = master_ob->interactive; ob->interactive->ob = ob; ob->flags |= O_ONCE_INTERACTIVE; master_ob->flags &= ~O_ONCE_INTERACTIVE; add_message(MESSAGE_FLUSH); master_ob->interactive = 0; free_object(master_ob, "reconnect"); add_ref(ob, "new_player"); command_giver = ob; #ifndef NO_IP_DEMON if (f_ip_demon_wr != NULL) { /*printf("sent hname %s\n:", query_ip_number(ob));*/ fprintf(f_ip_demon_wr, "%s\n", query_ip_number(ob)); fflush(f_ip_demon_wr); } #endif logon(ob); flush_all_player_mess(); return; } p = "Lpmud is full. Come back later.\r\n"; socket_write(new_socket, p, strlen(p)); socket_close(new_socket); } int call_function_interactive(i, str) struct interactive *i; char *str; { char *function; struct object *ob; int extra; if (!i->input_to) return 0; /* * Special feature: input_to() has been called to setup * a call to a function. */ if (i->noecho) { /* Must not enable echo before the user input is received. */ /* IAC == EOF . This can cause sprintf(), which is used in * add_message, to abort output after EOF. therefore, don't try to * send anything after the IAC in the same call to add_message. */ SEND_TELNET_COMMAND( add_message("%c", IAC); add_message("%c%c", WONT, TELOPT_ECHO); add_message(MESSAGE_FLUSH); ) i->noecho = 0; } function = i->input_to->function; ob = i->input_to->ob; if (ob->flags & O_DESTRUCTED) { /* Sorry, the object has selfdestructed ! */ free_input_to(i->input_to); i->input_to = 0; return 0; } free_object(ob, "call_function_interactive"); push_volatile_string(str); if (extra = i->input_to->num_arg) { extern struct svalue *inter_sp; memcpy( (char *)(inter_sp + 1), (char *)i->input_to->arg, extra * sizeof *inter_sp ); inter_sp += extra; } xfree((char *)i->input_to); /* * We must clear this reference before the call to apply(), because * someone might want to set up a new input_to(). */ i->input_to = 0; /* * Now we set current_object to this object, so that input_to will * work for static functions. */ current_object = ob; (void)apply(function, ob, 1 + extra); free_string(function); return 1; } int set_call(ob, it, noecho) struct object *ob; struct input_to *it; int noecho; { struct object *save; if (ob == 0 || it == 0) return 0; if (ob->interactive == 0 || ob->interactive->input_to) return 0; ob->interactive->input_to = it; ob->interactive->noecho = noecho; if (noecho) { save = command_giver; command_giver = ob; SEND_TELNET_COMMAND( add_message("%c", IAC); add_message("%c%c", WILL, TELOPT_ECHO); add_message(MESSAGE_FLUSH); ) command_giver = save; } return 1; } #if 0 void show_info_about(str, room, i) char *str, *room; struct interactive *i; { struct hostent *hp = 0; #if 0 hp = gethostbyaddr(&i->addr.sin_addr.s_addr, 4, AF_INET); #endif add_message("%-15s %-15s %s\n", hp ? hp->h_name : inet_ntoa(i->addr.sin_addr), str, room); } #endif void remove_all_players() { int i; extern int32 initial_eval_cost, eval_cost, assigned_eval_cost; for (i=0; i<MAX_PLAYERS; i++) { if (all_players[i] == 0) continue; command_giver = all_players[i]->ob; trace_level |= all_players[i]->trace_level; CLEAR_EVAL_COST; push_object(all_players[i]->ob); (void)apply_master_ob("remove_player", 1); if ( !(all_players[i]->ob->flags & O_DESTRUCTED) ) { emergency_destruct(all_players[i]->ob); } } } void set_prompt(str) char *str; { struct svalue *promptp; promptp = &command_giver->interactive->prompt; free_svalue(promptp); promptp->type = T_STRING; promptp->x.string_type = STRING_CONSTANT; promptp->u.string = str; } struct svalue *query_prompt(ob) struct object *ob; { return &ob->interactive->prompt; } /* * Print the prompt, but only if input_to not is disabled. */ void print_prompt() { if (command_giver == 0) fatal("command_giver == 0.\n"); if (command_giver->interactive->input_to == 0) { extern struct svalue *inter_sp; struct svalue *prompt; prompt = &command_giver->interactive->prompt; if (prompt->type == T_CLOSURE) { call_lambda(prompt, 0); prompt = inter_sp; if (prompt->type != T_STRING) { free_svalue(prompt); } else { /* beware: add_message() might cause an error. Thus, the LPC * stack has to include the prompt to free it then. */ add_message("%s", prompt->u.string); free_string_svalue(prompt); } inter_sp--; } else { add_message("%s", prompt->u.string); } } flush_all_player_mess(); } #if 0 /* * Let object 'me' snoop object 'you'. If 'you' is 0, then turn off * snooping. */ void set_snoop(me, you) struct object *me, *you; { struct interactive *on = 0, *by = 0, *tmp; int i; if (me->flags & O_DESTRUCTED) return; if (you && (you->flags & O_DESTRUCTED)) return; for(i=0; i<MAX_PLAYERS && (on == 0 || by == 0); i++) { if (all_players[i] == 0) continue; if (all_players[i]->ob == me) by = all_players[i]; else if (all_players[i]->ob == you) on = all_players[i]; } if (you == 0) { if (by == 0) error("Could not find myself to stop snoop.\n"); add_message("Ok.\n"); if (by->snoop_on == 0) return; by->snoop_on->snoop_by = 0; by->snoop_on = 0; return; } if (on == 0 || by == 0) { add_message("Failed.\n"); return; } if (by->snoop_on) { by->snoop_on->snoop_by = 0; by->snoop_on = 0; } if (on->snoop_by) { add_message("Busy.\n"); return; } /* * Protect against snooping loops. */ for (tmp = on; tmp; tmp = tmp->snoop_on) { if (tmp == by) { add_message("Busy.\n"); return; } } on->snoop_by = by; by->snoop_on = on; add_message("Ok.\n"); return; } #endif /* * 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 player 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. * A return value of -1 designates failure due to snooping loop. */ int new_set_snoop(me, you) struct object *me, *you; { struct interactive *on = 0, *by = 0, *tmp; int i; struct svalue *ret; /* 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 & snopee */ for(i = 0 ; i < MAX_PLAYERS && (on == 0 || by == 0); i++) { if (all_players[i] == 0) continue; if (all_players[i]->ob == me) by = all_players[i]; else if (all_players[i]->ob == you) on = all_players[i]; } #ifdef NATIVE_MODE /* Illegal snoop attempt by null object */ /* Amylaar: is this really needed? I think this would be a job for the * master. */ if (!current_object->eff_user) return 0; #endif /* Check for permissions with valid_snoop in master */ push_object(me); if (you == 0) push_number(0); else push_object(you); ret = apply_master_ob("valid_snoop", 2); if (!ret || ret->type != T_NUMBER || ret->u.number == 0) return 0; /* Stop snoop */ if (you == 0) { if (by == 0) error("Could not find snooper to stop snoop on.\n"); if (by->snoop_on == 0) return 1; by->snoop_on->snoop_by = 0; by->snoop_on = 0; return 1; } /* Strange event, but possible, so test for it */ if (on == 0) return 0; /* Protect against snooping loops */ for (tmp = on; tmp; tmp = tmp->snoop_on) { if (tmp == by) return -1; } /* Terminate previous snoop, if any */ if (on->snoop_by) { if (on->snoop_by->interactive) { on->snoop_by->interactive->snoop_on = 0; } else { free_object(on->snoop_by, "new_set_snoop"); } on->snoop_by = 0; } if (by) { if (by->snoop_on) { by->snoop_on->snoop_by = 0; by->snoop_on = 0; } by->snoop_on = on; } else { add_ref(me, "new_set_snoop"); } on->snoop_by = me; return 1; } #define TS_DATA 0 #define TS_IAC 1 #define TS_WILL 2 #define TS_WONT 3 #define TS_DO 4 #define TS_DONT 5 void telnet_neg(to, from) char *to, *from; { int state = TS_DATA; int ch; char *first = to; while(1) { ch = (*from++ & 0xff); switch(state) { case TS_DATA: switch(ch) { case IAC: state = TS_IAC; continue; case '\b': /* Backspace */ case 0x7f: /* Delete */ if (to <= first) continue; to -= 1; continue; default: if (ch & 0x80) { if (d_flag) debug_message("Tel_neg: 0x%x\n", ch); continue; } *to++ = ch; if (ch == 0) return; continue; } case TS_IAC: switch(ch) { case WILL: state = TS_WILL; continue; case WONT: state = TS_WONT; continue; case DO: state = TS_DO; continue; case DONT: state = TS_DONT; continue; case DM: break; case NOP: case GA: default: break; } state = TS_DATA; continue; case TS_WILL: if (d_flag) debug_message("Will %s\n", telopts[ch]); state = TS_DATA; continue; case TS_WONT: if (d_flag) debug_message("Wont %s\n", telopts[ch]); state = TS_DATA; continue; case TS_DO: if (d_flag) debug_message("Do %s\n", telopts[ch]); state = TS_DATA; continue; case TS_DONT: if (d_flag) debug_message("Dont %s\n", telopts[ch]); state = TS_DATA; continue; default: if (d_flag) debug_message("Bad state: 0x%x\n", state); state = TS_DATA; continue; } } } #define IPSIZE 200 static struct ipentry { long addr; char *name; } iptable[IPSIZE] = { {0, 0}, }; char *query_ip_name(ob) struct object *ob; { int i; if (ob == 0) ob = command_giver; if (!ob || ob->interactive == 0) return 0; 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); } #ifndef NO_IP_DEMON static void add_ip_entry(addr, name) long addr; char *name; { static int ipcur = 0; 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; } void nonblock_by_ip_demon() { set_socket_nonblocking(fileno(f_ip_demon)); } #endif #ifdef MALLOC_smalloc void count_iptable_ref() { int i; for(i = 0; i < IPSIZE; i++) { if (iptable[i].name) count_ref_from_string(iptable[i].name); } } #endif char *query_ip_number(ob) struct object *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(ad) struct in_addr ad; { unsigned char * p; int a, b, c, d; static char addr[20]; /* 16 + 1 should be enough */ p = (unsigned char *)&ad.s_addr; a = p[0]; b = p[1]; c = p[2]; d = p[3]; sprintf(addr, "%d.%d.%d.%d", a, b, c, d); return addr; } #endif /* INET_NTOA_OK */ char *query_host_name() { static char name[20]; gethostname(name, sizeof name); name[sizeof name - 1] = '\0'; /* Just to make sure */ return name; } struct object *query_snoop(ob) struct object *ob; { if (ob->interactive->snoop_by == 0) return 0; return ob->interactive->snoop_by; } int query_idle(ob) struct object *ob; { return current_time - ob->interactive->last_time; } void notify_no_command() { char *p,*m; if (!command_giver->interactive) return; p = command_giver->interactive->default_err_message; if (p) { m = process_string(p); /* We want 'value by function call' /JnA */ if (!shadow_catch_message(command_giver, m)) add_message("%s", m); if (m != p) xfree(m); free_string(p); command_giver->interactive->default_err_message = 0; } else { add_message("What ?\n"); } } void clear_notify() { if (!command_giver->interactive) return; if (command_giver->interactive->default_err_message) { free_string(command_giver->interactive->default_err_message); command_giver->interactive->default_err_message = 0; } } void set_notify_fail_message(str) char *str; { if (!command_giver || !command_giver->interactive) return; clear_notify(); if (command_giver->interactive->default_err_message) free_string(command_giver->interactive->default_err_message); command_giver->interactive->default_err_message = make_shared_string(str); } int replace_interactive(ob, obfrom, /*IGN*/name) struct object *ob; struct object *obfrom; char *name; { /* marion * i see no reason why to restrict this, besides - the length * (was) missing to strncmp() * JnA: There is every reason to restrict this. * Otherwise I can write my own player object without any security * at all! */ struct svalue *v; struct interactive *stale_interactive; push_constant_string(name); v = apply_master_ob("valid_exec", 1); if (!v || v->type != T_NUMBER || v->u.number == 0) return 0; /* if (strcmp(name, "secure/login.c") != 0) return 0; */ /* fprintf(stderr,"DEBUG: %s,%s\n",ob->name,obfrom->name); */ if ((stale_interactive = ob->interactive) && !stale_interactive->do_close) error("Bad argument1 to exec()\n"); if (!obfrom->interactive) error("Bad argument2 to exec()\n"); if (obfrom->interactive->message_length) { struct object *save; save=command_giver; command_giver=obfrom; add_message(MESSAGE_FLUSH); command_giver=save; } if (obfrom->interactive->snoop_on) { obfrom->interactive->snoop_on->snoop_by = ob; } ob->interactive = obfrom->interactive; ob->interactive->ob = ob; ob->interactive->catch_tell_activ = 1; if (obfrom->interactive = stale_interactive) { if (stale_interactive->snoop_on) { stale_interactive->snoop_on->snoop_by = obfrom; } stale_interactive->ob = obfrom; stale_interactive->catch_tell_activ = 1; } else { add_ref(ob, "exec"); free_object(obfrom, "exec"); ob->flags |= O_ONCE_INTERACTIVE; obfrom->flags &= ~O_ONCE_INTERACTIVE; } if (obfrom == command_giver) command_giver = ob; else if (ob == command_giver) command_giver = obfrom; return 1; } #ifdef DEBUG /* * This is used for debugging reference counts. */ void update_ref_counts_for_players() { int i; for (i=0; i<MAX_PLAYERS; i++) { struct object *ob; struct ed_buffer *buf; struct input_to *it; if (all_players[i] == 0) continue; all_players[i]->ob->extra_ref++; if (ob = all_players[i]->snoop_by) { if (ob->interactive == 0) { /* snooping monster */ ob->extra_ref++; } } /* end of snoop-processing */ if (buf = all_players[i]->ed_buffer) { count_ed_buffer_extra_refs(buf); } /* end of ed-buffer processing */ if (it = all_players[i]->input_to) { it->ob->extra_ref++; count_extra_ref_in_vector(it->arg, it->num_arg); } if (ob = all_players[i]->modify_command) count_extra_ref_in_object(ob); count_extra_ref_in_vector(&all_players[i]->prompt, 1); } } #endif /* DEBUG */ #ifdef MUDWHO char mudwhoid[200]; void sendmudwhoinfo() { struct object *ob; int i; static int last_called_time; if (current_time - last_called_time < MUDWHO_REFRESH_TIME) return; last_called_time = get_current_time(); rwhocli_pingalive(); for (i = 0; i < num_player; i++) { ob = get_interactive_object(i); if (ob->living_name) { sscanf(ob->name,"%*[^#]#%s",mudwhoid); strcat(mudwhoid,"@"); strcat(mudwhoid,MUDWHO_NAME); rwhocli_userlogin(mudwhoid,ob->living_name, ob->interactive->login_time); } } } void sendmudwhologout(ob) struct object *ob; { if (ob->interactive) { sscanf(ob->name,"%*[^#]#%s",mudwhoid); strcat(mudwhoid,"@"); strcat(mudwhoid,MUDWHO_NAME); rwhocli_userlogout(mudwhoid); } } #endif #ifdef UDP_SEND /* * Send messages to other muds. * * The message is sent with udp. If it gets there it gets there... */ int send_udp(to_host, to_port, msg) char *to_host; int to_port; char *msg; { int ip1, ip2, ip3, ip4; struct sockaddr_in name; struct hostent *hp; if (udp_send < 0) { /* Create socket on which to send. */ udp_send = socket(AF_INET, SOCK_DGRAM, 0); } if (udp_send < 0) return 0; if (sscanf(to_host, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) { name.sin_addr.s_addr = inet_addr(to_host); name.sin_family = AF_INET; } else { hp = gethostbyname(to_host); if (hp == 0) return 0; memcpy(&name.sin_addr, hp->h_addr, hp->h_length); name.sin_family = AF_INET; } name.sin_port = htons(to_port); /* Send message. */ #ifndef SENDTO_BROKEN if (sendto(udp_send, msg, strlen(msg), 0, (struct sockaddr *)&name, sizeof(name)) != strlen(msg)) #endif return 0; return 1; } #endif /* UDP_SEND */