/*
....[@@@..[@@@..............[@.................. 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 );
}