/* ....[@@@..[@@@..............[@.................. 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@falcon.mercer.peachnet.edu MUD++ development mailing list mudpp-list@spice.com ------------------------------------------------------------------------------ socket.cc */ #include <ctype.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 IStream 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 }; */ Socket::Socket( int port ) { int size = sizeof( sockaddr_in ); if( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { err = SOCK_NOT_CREATED; return; } if( fcntl( sock, F_SETFL, O_NONBLOCK ) == -1 ) { err = SOCK_FCNTL_ERR; return; } if( ( ::getsockname( sock, ( sockaddr * )this, &size ) ) == -1 ) { // err = SOCK_CANT_GET_NAME; return; } 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( char * address, int port ) { strcpy( ip, address ); sin_port = htons( port ); sin_family = AF_INET; if( isdigit( *address ) ) { if( (long)( sin_addr.s_addr = inet_addr( address ) ) == -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 ) ); } if( ( sock = ::socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { perror( "socket" ); err = SOCK_NOT_CREATED; return; } if( fcntl( sock, F_SETFL, O_NONBLOCK ) == -1 ) { perror( "fcntl" ); err = SOCK_FCNTL_ERR; return; } err = 0; } // This is for recreating a socket on a live TCP connection after a // reboot. Socket::Socket( char *address, int port, int desc ) { sock = desc; sin_port = htons( port ); sin_family = AF_INET; if( !*address || !strcmp( address, "0.0.0.0" ) ) { strcpy( ip, "0.0.0.0" ); return; } strcpy( ip, address ); if( isdigit( *ip ) ) { if( (long)( sin_addr.s_addr = inet_addr( ip ) ) == -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 ) ); } } int Socket::listen() { if( ( ::bind( sock, ( sockaddr * )this, sizeof( sockaddr_in ) ) ) == -1 ) { // err = SOCK_CANT_BIND; return -1; } if( ( ::listen( 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 ) ) == -1 ) { // err = SOCK_CANT_ACCEPT; return 0; } newSock->setDesc( 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() { hostent * he = ::gethostbyaddr( (char *) &sin_addr, sizeof( sin_addr ), AF_INET ); if( he ) strcpy( ip, he->h_name ); else { 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::getPeerName() { int size = sizeof( sockaddr ); if( ::getpeername( sock, ( sockaddr * ) this, &size ) == -1 ) { // err = SOCK_CANT_GET_PEER_NAME 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::noDelay() { if( fcntl( sock, F_SETFL, FNDELAY ) == -1 ) { err = SOCK_FCNTL_ERR; return -1; } return 0; } int Socket::nonBlock() { if( fcntl( sock, F_SETFL, O_NONBLOCK ) == -1 ) { err = SOCK_FCNTL_ERR; return -1; } return 0; } int Socket::connect() { if( ::connect( sock, (sockaddr *)this, sizeof( sockaddr ) ) ) { if( errno != EINPROGRESS ) { err = SOCK_NOT_CONNECTED; return -1; } } return 0; } int Socket::write( const char * buf ) { return write( buf, strlen( buf ) ); } int Socket::write( const char * buf, int size ) { int error; int bytes = 0; int max_write = 1024; if( size < max_write ) max_write = size; while( 1 ) { if( ( error = ::write( sock, ( buf + bytes ), max_write ) ) != -1 ) { bytes += error; if( bytes >= size ) return bytes; else max_write = size - bytes; continue; } else { if( errno == EAGAIN ) continue; else return -1; } } } int Socket::read( char * buf, int max ) { int bytes = 0; int error; while( 1 ) { if( ( error = ::read( sock, buf, ( max - bytes ) ) ) > 0 ) { bytes += error; *(buf + bytes) = '\0'; } else if( !error ) { *( buf + bytes ) = '\0'; err = SOCK_EOF; return bytes; } else { if( errno == EAGAIN ) { *(buf + bytes ) = '\0'; return bytes; } else if( bytes > 0 ) { *(buf + bytes ) = '\0'; return bytes; } return -1; } } } int Socket::echoOff() { return write( REQ_ECHO_OFF, 3 ); } int Socket::echoOn() { return write( REQ_ECHO_ON, 3 ); }