/*
....[@@@..[@@@..............[@.................. 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.h
*/
#ifndef _SOCKET_H
#define _SOCKET_H
#ifndef WIN32
#ifdef __WIN32__
#define WIN32
#endif
#endif
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#if !defined(WIN32)
#include <sys/socket.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/telnet.h>
#include <arpa/inet.h>
#include <netdb.h>
#else
#include "win32/telnet.h"
#endif
// You can remove io.h to use socket stuff standalone without io.h and io.cc
// It will try to use the standard C++ iostreams instead. You may have to
// add a -l or remove the -nostdinc++ to link your io lib or libstdc++.a
#ifdef _STANDALONE_
#include <iostream.h>
#define Cout cout // support for standard C++ lib to work outside of MUD++
#define mudpp_exit exit
#else
#include "io.h"
#endif
// 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
};
*/
#ifdef ultrix
extern "C"
{
int socket( int, int, int );
int setsockopt( int, int, int, void *, int );
int connect( int, struct sockaddr *, int );
int accept( int, struct sockaddr *, int * );
int bind( int, struct sockaddr *, int );
int listen( int, int );
int select( int, fd_set *, fd_set *, fd_set *, struct timeval * );
int getsockname( int, struct sockaddr *, int * );
int getpeername( int, struct sockaddr *, int * );
void bzero( char *, int );
}
#endif
#ifdef sun
extern "C"
{
int select( int, fd_set *, fd_set *, fd_set *, struct timeval * );
void bzero( char *, int );
int socketpair( int, int, int, int [2] );
}
#endif
#ifndef FNDELAY
#define FNDELAY O_NDELAY
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif
#ifndef EINPROGRESS
#define EINPROGRESS EWOULDBLOCK
#endif
#define MAX_READ 256
#define MAX_WRITE 4096
// Telnet codes.
const char WILL_ECHO [] =
{
(char) IAC,
(char) WILL,
(char) TELOPT_ECHO,
(char) 0
};
const char WONT_ECHO [] =
{
(char) IAC,
(char) WONT,
(char) TELOPT_ECHO,
(char) 0
};
const char WILL_SUPPRESS_GA [] =
{
(char) IAC,
(char) WILL,
(char) TELOPT_SGA,
(char) 0
};
const char WONT_SUPPRESS_GA [] =
{
(char) IAC,
(char) WONT,
(char) TELOPT_SGA,
(char) 0
};
const char TELNET_GA [] =
{
(char) IAC,
(char) GA,
(char) 0
};
// These codes aren't really accurate but it is enough for the class to work
#define SOCK_NOT_CREATED 1
#define SOCK_FCNTL_ERR 2
#define SOCK_UNKNOWN_HOST 3
#define SOCK_BAD_ADDRESS 4
#define SOCK_NOT_CONNECTED 5
#define SOCK_SOCKOPT_ERR 6
#define SOCK_EOF 7
class Descriptor
{
protected:
int fd;
bool eofd;
public:
Descriptor()
: fd(-1), eofd(false)
{
}
Descriptor( int x )
: fd(x), eofd(false)
{
}
int operator = ( int x ) { return ( fd = x ); }
operator int () { return fd; }
bool isOpen() { if( fd >= 0 ) return true; return false; }
int read( char *, int );
int recvfrom( char *, int, int, struct sockaddr *, int * );
int sendto( const char *, int, int, const struct sockaddr *, int );
int write( const char *, int );
int write( const char * buf ) { return write( buf, strlen( buf ) ); }
int close()
{ eofd = true; int i = fd; fd = -1;
#ifndef WIN32
return ::close( i );
#else
return ::closesocket(i);
#endif
}
bool eof() { return eofd; }
bool canWrite();
bool canRead();
bool canWriteWait( long, long );
bool canReadWait( long, long );
int noDelay();
int nonBlock();
};
inline bool Descriptor::canWrite()
{
timeval tzero;
tzero.tv_sec = 0;
tzero.tv_usec = 0;
fd_set W_SET;
FD_ZERO( &W_SET );
FD_SET( fd, &W_SET );
while( select( fd+1, 0, &W_SET, 0, &tzero ) < 0 )
{
#ifndef WIN32
if( errno == EINTR )
continue;
return false;
#else
int err = WSAGetLastError();
Cout << "select winsock error " << endl;
return false;
#endif
}
return (bool)FD_ISSET( fd, &W_SET );
}
inline bool Descriptor::canRead()
{
timeval tzero;
tzero.tv_sec = 0;
tzero.tv_usec = 0;
fd_set R_SET;
FD_ZERO( &R_SET );
FD_SET( fd, &R_SET );
while( select( fd+1, &R_SET, 0, 0, &tzero ) < 0 )
{
#ifndef WIN32
if( errno == EINTR )
continue;
return false;
#else
int err = WSAGetLastError();
Cout << "select winsock error " << endl;
return false;
#endif
}
return (bool)FD_ISSET( fd, &R_SET );
}
inline bool Descriptor::canWriteWait( long sec, long usec )
{
timeval tzero;
tzero.tv_sec = sec;
tzero.tv_usec = usec;
fd_set W_SET;
FD_ZERO( &W_SET );
FD_SET( fd, &W_SET );
while( select( fd+1, 0, &W_SET, 0, &tzero ) < 0 )
{
#ifndef WIN32
if( errno == EINTR )
continue;
return false;
#else
int err = WSAGetLastError();
Cout << "select winsock error " << endl;
return false;
#endif
}
return (bool)FD_ISSET( fd, &W_SET );
}
inline bool Descriptor::canReadWait( long sec, long usec )
{
timeval tzero;
tzero.tv_sec = sec;
tzero.tv_usec = usec;
fd_set R_SET;
FD_ZERO( &R_SET );
FD_SET( fd, &R_SET );
while( select( fd+1, &R_SET, 0, 0, &tzero ) < 0 )
{
#ifndef WIN32
if( errno == EINTR )
continue;
return false;
#else
int err = WSAGetLastError();
Cout << "select winsock error " << endl;
return false;
#endif
}
return (bool)FD_ISSET( fd, &R_SET );
}
class Socket : public sockaddr_in
{
protected:
Descriptor sock;
int err;
int sock_type;
char ip[ 128 ]; // char string version of ip address.
sockaddr_in foreign; // for UDP stuff
#ifdef WIN32
static WSADATA winfo;
int startupWinsock();
#endif
public:
Socket()
: err(0), sock_type(SOCK_STREAM)
{
strcpy( ip, "0.0.0.0" ); // 0.0.0.0 = INADDR_ANY
sin_port = 0;
sin_family = AF_INET;
sin_addr.s_addr = htonl( INADDR_ANY );
}
Socket( int type, int port );
Socket( int type, const char * address, int port );
Socket( const char * address, int port, int desc );
virtual ~Socket()
{
if( sock >= 0 )
sock.close();
}
void close()
{
if( sock >= 0 )
sock.close();
sock = -1;
}
int error() { return err; }
int eof() { return sock.eof(); }
int getSockType() { return sock_type; }
bool isUDP() { return ( sock_type == SOCK_DGRAM ); }
bool isTCP() { return ( sock_type == SOCK_STREAM ); }
char *getHostName() { return ip; }
int getFamily() { return sin_family; }
void setDescriptor( int x ) { sock = x; }
int getDescriptor() { return (int)sock; }
int reuseAddr();
int noDelay() { return sock.noDelay(); }
int nonBlock() { return sock.nonBlock(); }
int getPort() { return ntohs( sin_port ); }
int open();
Socket * accept();
int resolveIP();
int connect();
int getSockName();
int getPeerName();
int write( const char * );
int write( const char *, int );
int read( char *, int );
int send( const char *, int );
int recv( char *, int );
int willEcho();
int wontEcho();
int wontSuppressGA();
int willSuppressGA();
bool canRead() { return sock.canRead(); }
bool canWrite() { return sock.canWrite(); }
bool canReadWait( long s, long u ) { return sock.canReadWait( s, u ); }
bool canWriteWait( long s, long u ) { return sock.canWriteWait( s, u ); }
};
inline int Socket::write( const char * buf )
{ return sock.write( buf ); }
inline int Socket::write( const char * buf, int bytes )
{ return sock.write( buf, bytes ); }
inline int Socket::read( char * buf, int max )
{ return sock.read( buf, max ); }
inline int Socket::send( const char * buf, int bytes )
{ return sock.sendto( buf, bytes, 0,
(struct sockaddr*)&foreign, sizeof(foreign) ); }
inline int Socket::recv( char * buf, int bytes )
{ return sock.recvfrom( buf, bytes, 0,
(struct sockaddr*)&foreign, 0 ); }
const long DEFAULT_BUFSIZE = 2048;
class Buf
{
protected:
char * _buf;
char * _front;
long _buf_size;
long _bytes;
public:
Buf()
: _buf( new char[ DEFAULT_BUFSIZE ] ), _front( _buf ),
_buf_size( DEFAULT_BUFSIZE ),
_bytes(0)
{
}
Buf( long size )
: _buf( new char[ size ] ), _front( _buf ),
_buf_size( size ), _bytes(0)
{
}
bool setSize( long size );
void clr() { _bytes = 0; _front = _buf; }
void add( char * buf );
void add( char * buf, long bytes ); // faster
char * getLine( char * buf );
char * getLine( char * buf, long maxsize );
bool findLine( const char * str );
bool findStr( const char * str );
char * next() { return (_front + _bytes); }
void putLine( char * str );
void putStr( char * str );
void filter_isprint();
void filter_isascii();
};
// Could write as two classes, SocketInBuf, and SocketOutBuf, but
// most real world apps use 2-way communication so one class
// serves as well.
class SocketBuf : public Socket
{
protected:
Buf _inbuf;
Buf _outbuf;
public:
SocketBuf()
: Socket()
{
}
SocketBuf( int type, int port )
: Socket( type, port )
{
}
SocketBuf( int type, const char * address, int port )
: Socket( type, address, port )
{
}
SocketBuf( const char * address, int port, int desc )
: Socket( address, port, desc )
{
}
bool setInBufSize( long size ) { return _inbuf.setSize( size ); }
bool setOutBufSize( long size ) { return _outbuf.setSize( size ); }
long readBuf();
long writeBuf();
void clrInBuf() { _inbuf.clr(); }
void clrOutBuf() { _outbuf.clr(); }
void clrBufs() { _outbuf.clr(); _inbuf.clr(); }
char * getLine( char * buf ) { return _inbuf.getLine( buf ); }
char * getLine( char * buf, long maxsize )
{ return _inbuf.getLine( buf, maxsize ); }
bool gotLine( const char * str ) { return _inbuf.findLine( str ); }
bool gotStr( const char * str ) { return _inbuf.findStr( str ); }
void putLine( char * str );
void putStr( char * str );
// under construction
};
#endif