#include "define.h" #include "struct.h" #include "telnet.h" int socket_one; int socket_two; bool wizlock = FALSE; link_data* link_list = NULL; link_data* link_next; bool process_output ( link_data* ); bool read_link ( link_data* ); void stop_idling ( char_data* ); const char go_ahead_str [] = { (const char) IAC, (const char) GA, '\0' }; const char echo_off_str [] = { (const char) IAC, (const char) WILL, (const char) TELOPT_ECHO, '\0' }; const char echo_on_str [] = { (const char) IAC, (const char) WONT, (const char) TELOPT_ECHO, '\0' }; struct in_addr *get_bind_addr(); /* * PREVIOUS PROCESS SOCKET ROUTINES */ int write(int channel, const char *text, unsigned int length) { return send(channel, text, length, 0); } int read(int channel, char *buffer, unsigned int length) { return recv(channel, buffer, length, 0); } void recover_links( ) { /* rlimit lim; int i; link_data* link; if( getrlimit( RLIMIT_NOFILE, &lim ) != 0 ) panic( "Init_Network: error getting file number." ); for( i = 3; i < int( lim.rlim_cur ); i++ ) if( int( write( i, "\r\n", 2 ) ) != - 1 ) { link = new link_data; link->channel = i; link->connected = CON_INTRO; link->next = link_list; link_list = link; } echo( "-- New process started. --\r\n" ); */ return; } void restart_links( ) { link_data* link; struct sockaddr_in net_addr_in; int addrlen; for( ; ; ) { if( ( link = link_list ) == NULL ) return; link_list = link_list->next; addrlen = sizeof( net_addr_in ); if( getpeername( link->channel, (struct sockaddr*) &net_addr_in, &addrlen ) == -1 ) panic( "Open_Link: Error returned by getpeername." ); write_host( link, (char*) &net_addr_in.sin_addr ); } } /* * SOCKET ROUTINES */ void open_link( int port ) { link_data* link; struct sockaddr_in net_addr; int addrlen; int fd_conn; addrlen = sizeof( net_addr ); fd_conn = accept( port, (struct sockaddr *) &net_addr, &addrlen ); if( fd_conn < 0 ) return; unsigned long val = 1; ioctlsocket(fd_conn, FIONBIO, &val); link = new link_data; link->channel = fd_conn; link->connected = CON_INTRO; write_host( link, (char*) &net_addr.sin_addr ); return; } struct in_addr *get_bind_addr() { static struct in_addr bind_addr; /* Clear the structure */ memset((char *) &bind_addr, 0, sizeof(bind_addr)); bind_addr.s_addr = htonl(INADDR_ANY); printf("Binding to all IP interfaces on this host.\r\n"); return &bind_addr; } int open_port( int portnum ) { struct sockaddr_in server; struct linger sock_linger; struct hostent* host; char* hostname = static_string( ); int sock; int i = 1; int sz = sizeof( int ); WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1); if (WSAStartup(wVersionRequested, &wsaData) != 0) panic("SYSERR: WinSock not available!"); sock_linger.l_onoff = 1; sock_linger.l_linger = 0; if( gethostname( hostname, THREE_LINES ) != 0 ) panic( "Open_Port: Gethostname failed." ); printf( "Open_Port: gethostname: %s.\r\n", hostname ); if( ( host = gethostbyname( hostname ) ) == NULL ) panic( "Open_Port: Error in gethostbyname." ); if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) panic("SYSERR: Error opening network connection: Winsock error #%d", WSAGetLastError()); if( setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char*) &i, sz ) < 0 ) panic( "Open_port: error in setsockopt (SO_REUSEADDR)" ); if( setsockopt( sock, SOL_SOCKET, SO_LINGER, (char*) &sock_linger, sizeof( sock_linger ) ) < 0 ) panic( "Open_port: error in setsockopt (SO_LINGER)" ); unsigned long val = 1; if (ioctlsocket(sock, FIONBIO, &val) != 0) panic ("Open_port: Error setting nonblocking."); // if( fcntl( sock, F_SETFD, 1 ) == -1 ) // panic( "Open_port: Error setting close on Exec." ); memset( &server, 0, sizeof( struct sockaddr_in ) ); server.sin_family = AF_INET;; server.sin_port = htons( portnum ); server.sin_addr = *get_bind_addr(); // memcpy( &server.sin_addr, host->h_addr_list[0], host->h_length ); if( bind( sock, (struct sockaddr*) ( &server ), sizeof( server ) ) ) panic( "Open_port: Error binding port %d at %s.", portnum, host->h_name ); if( listen( sock, 3 ) ) panic( "Open_port: error in listen" ); printf( "Binding port %d at %s.\r\n", portnum, host->h_name ); return sock; } void update_links( void ) { char_data* ch; link_data* link; text_data* receive; fd_set read_set; fd_set write_set; fd_set exec_set; struct timeval start; struct timeval timeout; gettimeofday( &start, NULL ); timeout.tv_sec = 1; timeout.tv_usec = 0; FD_ZERO( &read_set ); FD_ZERO( &write_set ); FD_ZERO( &exec_set ); FD_SET( socket_one, &read_set ); FD_SET( socket_two, &read_set ); for( link = link_list; link != NULL; link = link->next ) { FD_SET( link->channel, &read_set ); FD_SET( link->channel, &write_set ); FD_SET( link->channel, &exec_set ); } if( (int) select( FD_SETSIZE, &read_set, &write_set, &exec_set, &timeout ) < 0 ) panic( "Update links: select" ); if( FD_ISSET( socket_one, &read_set ) ) open_link( socket_one ); if( FD_ISSET( socket_two, &read_set ) ) open_link( socket_two ); for( link = link_list; link != NULL; link = link_next ) { link_next = link->next; if( FD_ISSET( link->channel, &exec_set ) ) { write( link->player ); close_socket( link ); continue; } if( FD_ISSET( link->channel, &read_set ) ) { link->idle = 0; if( link->player != NULL ) link->player->timer = current_time; if( !read_link( link ) ) { write( link->player ); close_socket( link ); continue; } } if( link->idle++ > 10000 && link->connected != CON_PLAYING ) { send( link, "\r\n\r\n-- CONNECTION TIMEOUT --\r\n" ); close_socket( link, TRUE ); } } pulse_time[ TIME_READ_INPUT ] = stop_clock( start ); gettimeofday( &start, NULL ); for( link = link_list; link != NULL; link = link_next ) { link_next = link->next; if( link->command = ( ( receive = link->receive ) != NULL ) ) { ampersand( receive ); link->receive = receive->next; link->idle = 0; if( link->connected == CON_PLAYING ) { stop_idling( ch = link->character ); interpret( link->character, receive->message.text ); } else nanny( link, receive->message.text ); delete receive; } } pulse_time[ TIME_COMMANDS ] = stop_clock( start ); gettimeofday( &start, NULL ); for( link = link_list; link != NULL; link = link_next ) { link_next = link->next; if( link->idle%25 == 0 && FD_ISSET( link->channel, &write_set ) && !process_output( link ) ) { write( link->player ); close_socket( link ); } } pulse_time[ TIME_WRITE_OUTPUT ] = stop_clock( start ); return; } /* * CLOSING OF SOCKETS */ void extract( link_data* prev ) { link_data* link; for( link = link_list; link != NULL; link = link->next ) if( link->snoop_by == prev ) link->snoop_by = NULL; send( prev->snoop_by, "Your victim has left the game.\r\n" ); if( link_next == prev ) link_next = prev->next; remove( link_list, prev ); if( prev->account != NULL && prev->account->last_login == -1 ) extract( prev->account ); delete prev; return; } void close_socket( link_data* link, bool process ) { char buf [ MAX_STRING_LENGTH ]; char_data* ch; int connected = link->connected; if( link->channel == -1 ) { bug( "Close_Socket: Closing a dead socket??" ); return; } // unswitch if( ( ch = link->player ) != NULL && ch != link->character ) do_return( link->character, "" ); // send last of buffer if( process ) { link->connected = CON_CLOSING_LINK; process_output( link ); } // send EOF send(link->channel, empty_string, 1, (unsigned int) 0); if( ch != NULL ) { if( connected == CON_PLAYING ) { send_seen( ch, "%s has lost %s link.\r\n", ch, ch->His_Her( ) ); sprintf( buf, "%s has lost link.", ch->descr->name ); info( buf, LEVEL_IMMORTAL, buf, IFLAG_LOGINS, 1, ch ); ch->link = NULL; } else { if( ch->shdata->level == 0 && ch->pcdata->pfile != NULL ) extract( ch->pcdata->pfile, link ); ch->Extract( ); } } closesocket( link->channel ); extract( link ); return; } /* * INPUT ROUTINES */ bool erase_command_line( char_data* ch ) { char* tmp = static_string( ); if( ch == NULL || ch->pcdata->terminal == TERM_DUMB || ch->link->connected != CON_PLAYING || !is_set( ch->pcdata->pfile->flags, PLR_STATUS_BAR ) ) return TRUE; sprintf( tmp, "[%d;1H[J", ch->pcdata->lines ); if( int( write( ch->link->channel, tmp, strlen( tmp ) ) ) == -1 ) return FALSE; return TRUE; } bool read_link( link_data* link ) { char buf [ 2*MAX_INPUT_LENGTH+100 ]; text_data* receive; int length; int nRead; char* input; char* output; strcpy( buf, link->rec_pending ); length = strlen( buf ); nRead = read( link->channel, buf+length, 100 ); if( nRead <= 0 ) return FALSE; free_string( link->rec_pending, MEM_LINK ); link->rec_pending = empty_string; buf[ length+nRead ] = '\0'; if( length+nRead > MAX_INPUT_LENGTH-2 ) { if( link->connected != CON_PLAYING ) return FALSE; send( link->character, "!! Truncating input !!\r\n" ); sprintf( buf+MAX_INPUT_LENGTH-3, "\r\n" ); } for( input = output = buf; *input != '\0'; input++ ) { if( *input != '\n' ) { if( isascii( *input ) && isprint( *input ) ) *output++ = ( *input == '~' ? '-' : *input ); continue; } for( ; --output >= buf && *output == ' '; ); *(++output) = '\0'; if( link->connected != CON_PLAYING ) receive = new text_data( buf ); else if( *buf == '!' ) receive = new text_data( link->rec_prev ); else { receive = new text_data( subst_alias( link, buf ) ); free_string( link->rec_prev, MEM_LINK ); link->rec_prev = alloc_string( receive->message.text, MEM_LINK ); } append( link->receive, receive ); output = buf; if( !erase_command_line( link->character ) ) return FALSE; } *output = '\0'; link->rec_pending = alloc_string( buf, MEM_LINK ); return TRUE; } /* * OUTPUT ROUTINES */ bool process_output( link_data* link ) { text_data* output; text_data* next; char_data* ch = link->character; bool status_bar; if( link->connected == CON_PLAYING && ch == NULL ) { bug( "Process_Output: Link playing with null character." ); bug( "-- Host = '%s'", link->host ); bug( "-- Rec_Prev = '%s'", link->rec_prev ); return FALSE; } status_bar = ( link->connected == CON_PLAYING && is_set( ch->pcdata->pfile->flags, PLR_STATUS_BAR ) && ch->pcdata->terminal != TERM_DUMB ); if( link->send == NULL && !link->command ) return TRUE; /* SAVE CURSOR */ if( status_bar ) { next = link->send; link->send = NULL; scroll_window( ch ); if( next != NULL ) send( ch, "\r\n" ); cat( link->send, next ); prompt_ansi( link ); command_line( ch ); } else { if( !link->command ) { next = link->send; link->send = NULL; send( ch, "\r\n" ); cat( link->send, next ); } if( link->connected == CON_PLAYING && link->receive == NULL ) prompt_nml( link ); } /* SEND OUTPUT */ for( ; ( output = link->send ) != NULL; ) { if( int( write( link->channel, output->message.text, output->message.length ) ) == -1 ) return FALSE; link->send = output->next; delete output; } return TRUE; } /* * LOGIN ROUTINES */ typedef void login_func( link_data*, char* ); struct login_handle { login_func* function; int state; }; void nanny( link_data* link, char* argument ) { char_data* ch; int i; login_handle nanny_list [] = { { nanny_intro, CON_INTRO }, { nanny_acnt_name, CON_ACNT_NAME }, { nanny_acnt_password, CON_ACNT_PWD }, { nanny_acnt_email, CON_ACNT_EMAIL }, { nanny_acnt_enter, CON_ACNT_ENTER }, { nanny_acnt_confirm, CON_ACNT_CONFIRM }, { nanny_acnt_check, CON_ACNT_CHECK }, { nanny_acnt_check_pwd, CON_ACNT_CHECK_PWD }, { nanny_old_password, CON_PASSWORD_ECHO }, { nanny_old_password, CON_PASSWORD_NOECHO }, { nanny_motd, CON_READ_MOTD }, { nanny_imotd, CON_READ_IMOTD }, { nanny_new_name, CON_GET_NEW_NAME }, { nanny_acnt_request, CON_ACNT_REQUEST }, { nanny_acnt_menu, CON_ACNT_MENU }, { nanny_confirm_password, CON_CONFIRM_PASSWORD }, { nanny_set_term, CON_SET_TERM }, { nanny_show_rules, CON_READ_GAME_RULES }, { nanny_agree_rules, CON_AGREE_GAME_RULES }, { nanny_alignment, CON_GET_NEW_ALIGNMENT }, { nanny_help_alignment, CON_HELP_ALIGNMENT }, { nanny_disc_old, CON_DISC_OLD }, { nanny_help_class, CON_HELP_CLSS }, { nanny_class, CON_GET_NEW_CLSS }, { nanny_help_race, CON_HELP_RACE }, { nanny_race, CON_GET_NEW_RACE }, { nanny_stats, CON_DECIDE_STATS }, { nanny_help_sex, CON_HELP_SEX }, { nanny_sex, CON_GET_NEW_SEX }, { nanny_new_password, CON_GET_NEW_PASSWORD }, { nanny_acnt_enter, CON_CE_ACCOUNT }, { nanny_acnt_check_pwd, CON_CE_PASSWORD }, { nanny_acnt_email, CON_CE_EMAIL }, { nanny_acnt_enter, CON_VE_ACCOUNT }, { nanny_ve_validate, CON_VE_VALIDATE }, { nanny_acnt_confirm, CON_VE_CONFIRM }, { NULL, -1 } }; skip_spaces( argument ); ch = link->character; for( i = 0; nanny_list[i].function != NULL; i++ ) if( link->connected == nanny_list[i].state ) { nanny_list[i].function( link, argument ); return; } if( link->connected == CON_PAGE ) { write_greeting( link ); link->connected = CON_INTRO; return; } if( link->connected == CON_FEATURES ) { help_link( link, "Features_2" ); link->connected = CON_PAGE; return; } if( link->connected == CON_POLICIES ) { help_link( link, "Policy_2" ); link->connected = CON_PAGE; return; } if( link->connected == CON_DIGITALNATION ) { help_link( link, "Digitalnation_2" ); link->connected = CON_PAGE; return; } bug( "Nanny: bad link->connected %d.", link->connected ); close_socket( link ); return; } void stop_idling( char_data* ch ) { if( ch == NULL || ch->link == NULL || ch->link->connected != CON_PLAYING || ch->was_in_room == NULL ) return; ch->timer = current_time; if( ch->array != NULL ) ch->From( ); ch->To( ch->was_in_room ); ch->was_in_room = NULL; send_seen( ch, "%s has returned from the void.\r\n", ch ); return; } void write_greeting( link_data* link ) { help_link( link, "greeting" ); send( link, " Choice: " ); return; }