/* ....[@@@..[@@@..............[@.................. MUD++ is a written from ....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and ....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++. ....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing ....[@......[@..[@@@@@..[@@@@@.................. development project. All ................................................ contributions are welcome. ....Copyright(C).1995.Melvin.Smith.............. Enjoy. ------------------------------------------------------------------------------ Melvin Smith (aka Fusion) msmith@hom.net MUD++ development mailing list mudpp@van.ml.org ------------------------------------------------------------------------------ socket.cc */ #include <ctype.h> #include "config.h" #include "socket.h" // This is the beginning of redesigning the Server and all IO classes // The Socket class should be in the Stream class hierarchy but as // I have the hierarchy now, it is not easy to insert Socket since // Sockets can't be memory mapped so the MPPIStream class wont work with it. // The Read(), Write() functions are the same as Server:: functions and // will replace them when Socket class is done. The Server will then // have a slightly different role. /* Structures describing an Internet socket address from sys/in.h INADDR_ANY is 0.0.0.0 which means accept all incoming #define INADDR_ANY ((unsigned long int) 0x00000000) struct in_addr { __u32 s_addr; }; struct sockaddr_in { short int sin_family; Address family unsigned short int sin_port; Port number ( network byte order ) struct in_addr sin_addr; IP address unsigned char __pad[ XXX ]; Pads to size of sockaddr }; */ int Descriptor::write( const char * buf, int size ) { #ifndef WIN32 // UNIX int error; int bytes = 0; int max_write = 1024; if( size < max_write ) max_write = size; while( 1 ) { if( ( error = ::write( fd, ( buf + bytes ), max_write ) ) >= 0 ) { bytes += error; if( bytes >= size ) return bytes; else max_write = size - bytes; continue; } else { if( errno == EAGAIN || errno == EINTR ) continue; else return -1; } } return -1; #else // WIN32 int error; int bytes = 0; int max_write = 1024; if( size < max_write ) max_write = size; while( 1 ) { if( ( error = send( fd, (buf + bytes ), max_write, 0 ) ) != SOCKET_ERROR ) { bytes += error; if( bytes >= size ) return bytes; else max_write = size - bytes; continue; } else { int errcode = WSAGetLastError(); if( errcode == WSAEINTR || errcode == WSAEWOULDBLOCK ) continue; else break; } break; } return -1; #endif } int Descriptor::read( char * buf, int max ) { #ifndef WIN32 // UNIX int bytes = 0; int error; while( 1 ) { if( ( error = ::read( fd, buf + bytes, ( max - bytes ) ) ) > 0 ) bytes += error; else if( !error ) { eofd = true; return bytes; } else { if( errno == EAGAIN || errno == EWOULDBLOCK ) return bytes; else if( errno == ECONNRESET ) { eofd = true; return 0; } else if( errno == EINTR ) continue; return error; } } return -1; #else // WIN32 int bytes = 0; int error; while( 1 ) { if( ( error = recv( fd, buf + bytes, ( max - bytes ), 0 ) ) != SOCKET_ERROR ) { if( error > 0 ) bytes += error; else if( !error ) { eofd = true; return bytes; } } else { int errcode = WSAGetLastError(); if( errcode == WSAEWOULDBLOCK ) return bytes; else if( errcode == WSAECONNRESET ) { eofd = true; return 0; } else if( errcode == WSAEINTR ) continue; return error; } break; } return -1; #endif // WIN32 } // UDP stuff not finished, just cutting and pasting from // the TCP code, will finish soon. int Descriptor::sendto( const char * buf, int size, int flags, const struct sockaddr * to, int tolen ) { #if !defined(WIN32) int error; int bytes = 0; int max_write = 1024; if( size < max_write ) max_write = size; while( 1 ) { if( ( error = ::sendto( fd, ( buf + bytes ), max_write, flags, to, tolen ) ) >= 0 ) { bytes += error; if( bytes >= size ) return bytes; else max_write = size - bytes; continue; } else { if( errno == EAGAIN || errno == EINTR ) continue; else return -1; } } return -1; #else //WIN32 return -1; //for now #endif } // UDP stuff not finished, just cutting and pasting from // the TCP code, will finish soon. int Descriptor::recvfrom( char * buf, int max, int flags, struct sockaddr * from, int * fromlen ) { #if !defined(WIN32) int bytes = 0; int error; while( 1 ) { if( ( error = ::recvfrom( fd, buf + bytes, ( max - bytes ), flags, from, fromlen ) ) > 0 ) bytes += error; else if( !error ) { eofd = true; return bytes; } else { if( errno == EAGAIN || errno == EWOULDBLOCK ) return bytes; else if( errno == ECONNRESET ) { eofd = true; return 0; } else if( errno == EINTR ) continue; return error; } } return -1; #else //WIN32 return -1;//for now #endif } int Descriptor::noDelay() { #ifndef WIN32 return fcntl( fd, F_SETFL, FNDELAY ); #else return 0; // no corresponding function in winsock #endif // WIN32 } int Descriptor::nonBlock() { #ifndef WIN32 return fcntl( fd, F_SETFL, O_NONBLOCK ); #else u_long tempBlockVal = true; return ioctlsocket( fd, FIONBIO, &tempBlockVal ); //supposed to do same thing, to block change 1 to 0 #endif // WIN32 } #ifdef WIN32 WSADATA Socket::winfo; #endif Socket::Socket( int type, int port ) { if( type != SOCK_STREAM && type != SOCK_DGRAM ) { printf( "Invalid socket type.\n" ); mudpp_exit(0); } sock_type = type; #ifndef WIN32 if( ( sock = ::socket( AF_INET, sock_type, 0 ) ) == -1 ) { err = SOCK_NOT_CREATED; return; } #else // winsock SOCKET type is unsigned so INVALID_SOCKET e.g.(~0) is used if( ( sock = ::socket( AF_INET, sock_type, 0 ) ) == INVALID_SOCKET ) { // Be nice and try to init winsock for the lazy programmer // I am the lazy type myself. -Melvin if( WSAGetLastError() == WSANOTINITIALISED ) { startupWinsock(); if( ( sock = socket( AF_INET, sock_type, 0 ) ) == INVALID_SOCKET ) { Cout << "Error starting winsock.\n"; err = SOCK_NOT_CREATED; return; } } else { err = SOCK_NOT_CREATED; return; } } #endif // WIN32 strcpy( ip, "0.0.0.0" ); sin_port = htons( port ); sin_family = AF_INET; sin_addr.s_addr = htonl( INADDR_ANY ); err = 0; } Socket::Socket( int type, const char * address, int port ) { strcpy( ip, address ); sin_port = htons( port ); sin_family = AF_INET; if( type != SOCK_STREAM && type != SOCK_DGRAM ) { printf( "Invalid socket type.\n" ); mudpp_exit(0); } sock_type = type; if( isdigit( *address ) ) { if( ( sin_addr.s_addr = inet_addr( address ) ) == (unsigned int)-1 ) { perror( "inet_addr" ); err = SOCK_BAD_ADDRESS; return; } } else { struct hostent *he = ::gethostbyname( address ); if( !he ) { #ifndef WIN32 perror( "gethostbyname" ); err = SOCK_UNKNOWN_HOST; return; #else if( WSAGetLastError() == WSANOTINITIALISED ) { startupWinsock(); he = ::gethostbyname( address ); if( !he ) { err = SOCK_NOT_CREATED; Cout << "gethostbyname winsock error " << WSAGetLastError() << endl; return; } } else { err = SOCK_NOT_CREATED; Cout << "gethostbyname winsock error " << WSAGetLastError() << endl; return; } #endif } memcpy( (char*)&sin_addr, he->h_addr, sizeof( sin_addr ) ); } #ifndef WIN32 if( ( sock = ::socket( AF_INET, sock_type, 0 ) ) == -1 ) { perror( "socket" ); err = SOCK_NOT_CREATED; return; } #else if( ( sock = ::socket( AF_INET, sock_type, 0 ) ) == INVALID_SOCKET ) { // Be nice and try to init winsock for the lazy programmer // I am the lazy type myself. -Melvin if( WSAGetLastError() == WSANOTINITIALISED ) { startupWinsock(); if( ( sock = socket( AF_INET, sock_type, 0 ) ) == INVALID_SOCKET ) { Cout << "Error starting winsock.\n"; err = SOCK_NOT_CREATED; return; } } else { err = SOCK_NOT_CREATED; return; } } #endif // WIN32 err = 0; } // This is for recreating a socket on a live TCP connection after a // reboot. Socket::Socket( const char *address, int port, int desc ) { sock = desc; sin_port = htons( port ); sin_family = AF_INET; sock_type = SOCK_STREAM; if( !*address || !strcmp( address, "0.0.0.0" ) ) { strcpy( ip, "0.0.0.0" ); return; } strcpy( ip, address ); // These two calls don't necessarily stop the show if they fail // but some things might not work as planned. Need to think // more about how to handle failures here. getSockName(); getPeerName(); /* Testing - getSockName() should now take care of this. if( isdigit( *ip ) ) { if( ( sin_addr.s_addr = inet_addr( ip ) ) == (unsigned int)-1 ) { perror( "inet_addr" ); err = SOCK_BAD_ADDRESS; return; } } else { struct hostent *he; if( !( he = ::gethostbyname( address ) ) ) { perror( "gethostbyname" ); err = SOCK_UNKNOWN_HOST; return; } memcpy( (char*)&sin_addr, he->h_addr, sizeof( sin_addr ) ); } */ } #ifdef WIN32 int Socket::startupWinsock() { // winsock ver 1.1 //WORD ver = 0x0101; err = WSAStartup( (WORD)0x0101, &winfo ); if( err != 0 ) { Cout << "Winsock startup error " << WSAGetLastError() << endl; return -1; } return 0; } #endif int Socket::open() { #ifndef WIN32 if( ( ::bind( (int)sock, ( sockaddr * )this, sizeof( sockaddr_in ) ) ) == -1 ) #else if( ( ::bind( (int)sock, ( sockaddr * )this, sizeof( sockaddr_in ) ) ) == SOCKET_ERROR ) #endif // WIN32 { // err = SOCK_CANT_BIND; return -1; } // Skip listen() call for UDP sockets if( sock_type == SOCK_DGRAM ) return 0; if( ( ::listen( (int)sock, 5 ) ) == -1 ) { // err = SOCK_CANT_LISTEN; return -1; } return 0; } Socket * Socket::accept() { Socket * newSock = new Socket; int desc; int size = sizeof( sockaddr_in ); if( ( desc = ::accept( sock, ( sockaddr * ) newSock, &size ) ) < 0 ) { // err = SOCK_CANT_ACCEPT; delete newSock; return 0; } newSock->setDescriptor( desc ); if( newSock->getFamily() != AF_INET ) { // err = SOCK_INVALID_ADDRESS; newSock->close(); delete newSock; return 0; } if( newSock->noDelay() == -1 ) { // err = SOCK_CANT_SET_NODELAY; newSock->close(); delete newSock; return 0; } // Now get info for new connection if( newSock->getPeerName() == -1 ) { // err = SOCK_CANT_GET_PEER_NAME; delete newSock; return 0; } if( newSock->resolveIP() == -1 ) { delete newSock; return 0; } return newSock; } int Socket::resolveIP() { struct hostent * he = ::gethostbyaddr( (char *) &sin_addr, sizeof( sin_addr ), AF_INET ); if( he ) strcpy( ip, he->h_name ); else { #ifdef WIN32 #ifdef _DEBUG printf ("Socket::resolveIP:gethostbyaddr error: %i\n", WSAGetLastError()); #endif // _DEBUG #endif // WIN32 unsigned long addr = ntohl( sin_addr.s_addr ); if( (long) addr == -1 ) { *ip = '\0'; return -1; } sprintf( ip, "%ld.%ld.%ld.%ld", ( addr >> 24 ) & 0x000000ff, ( addr >> 16 ) & 0x000000ff, ( addr >> 8 ) & 0x000000ff, ( addr ) & 0x000000ff ); } return 0; } int Socket::getSockName() { int size = sizeof( sockaddr ); if( ::getsockname( sock, (sockaddr *)this, &size ) == -1 ) { perror( "Socket::getsockname" ); return -1; } return 0; } int Socket::getPeerName() { int size = sizeof( sockaddr ); if( ::getpeername( sock, (sockaddr *)this, &size ) == -1 ) { perror( "Socket::getPeerName" ); return -1; } return 0; } int Socket::reuseAddr() { int i; if( ::setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i) ) == -1 ) { err = SOCK_SOCKOPT_ERR; return -1; } return 0; } int Socket::connect() { if( sock_type != SOCK_STREAM ) { printf( "connect: socket is not TCP.\n" ); mudpp_exit(0); } if( ::connect( sock, (sockaddr *)this, sizeof( sockaddr ) ) ) { #ifdef WIN32 err = WSAGetLastError(); if( err != WSAEWOULDBLOCK ) { Cout << "connect: winsock error " << err << endl; err = SOCK_NOT_CONNECTED; #else if( errno != EWOULDBLOCK ) { err = SOCK_NOT_CONNECTED; #endif return -1; } } return 0; } int Socket::willEcho() { return write( WILL_ECHO, 3 ); } int Socket::willSuppressGA() { return write( WILL_SUPPRESS_GA, 3 ); } int Socket::wontEcho() { return write( WONT_ECHO, 3 ); } int Socket::wontSuppressGA() { return write( WONT_SUPPRESS_GA, 3 ); } bool Buf::setSize( long size ) { char * temp = _buf; _buf = new char[ size ]; if( ! _buf ) { _buf = temp; return false; } _buf_size = size; _front = _buf; return false; } // buf must be null terminated. // use add( buf, size ) for better performance void Buf::add( char * buf ) { strcpy( _front + _bytes, buf ); _bytes += strlen( buf ); } // buf does not have to be null terminated // this is the fastest way to add to buffer void Buf::add( char * buf, long bytes ) { // add checks for overflow and resize memcpy( _front + _bytes, buf, bytes ); _bytes += bytes; } void Buf::filter_isprint() { } char * Buf::getLine( char * line ) { char * ptr = line; char * save = _front; if( _bytes == 0 ) return 0; for( ; _bytes > 0 && *_front != '\n'; _front++, ptr++, _bytes-- ) *ptr = *_front; if( *_front != '\n' ) { _front = save; return 0; } *(++ptr) = 0; if( _bytes ) if( --_bytes && *(++_front) == '\r' ) _bytes--, _front++; return line; } // Add support for wildcards with % bool Buf::findLine( const char * line ) { return false; } // Add support for wildcards with % bool Buf::findStr( const char * str ) { if( _bytes <= 0 ) return false; int count = _bytes; int count2; const char * ptr1; char * ptr2 = _front; while( count ) { ptr1 = str; while( count && *ptr2 != *ptr1 ) ptr2++, count--; if( !count ) return false; count2 = strlen( str ) - 1; for( ; ; ptr1++, ptr2++, count--, count2-- ) { if( !count2 ) return true; if( !count ) return false; if( *ptr1 != *ptr2 ) break; } } return false; } long SocketBuf::readBuf() { long bytes; char buf[ 4096 ]; bytes = Socket::read( buf, 4095 ); if( bytes > 0 ) _inbuf.add( buf, bytes ); return bytes; }