campo-sirio/include/netsock.cpp

715 lines
16 KiB
C++
Raw Normal View History

#include <stdlib.h>
#define XVT_INCL_NATIVE
#include <progind.h>
#include <config.h>
// skstream.h
// Copyright (C) 1995, 1996 by John C. Wang. All Rights Reserved.
//
// You may distribute this file with your product in either of two ways:
// IN SOURCE CODE FORM: You must include this file in its entirety.
// IN OBJECT CODE FORM: You must give proper acknowledgements to the author(s)
// of this program. This may take the form of credits on the start-up screen.
//
// IN ANYCASE, THIS SOFTWARE IS DISTRIBUTED WITHOUT ANY KIND OF EXPLICIT OR
// IMPLICIT WARRANTIES AND THE AUTHORS ARE NOT RESPONSIBLE FOR ANY EVENTS THAT
// OCCURS AS A RESULT OF USING THIS SOFTWARE.
//
// History:
// [JCW 95-Dec-04] created
// [JCW 95-Dec-20] comments added for distribution 95a
// [JCW 96-Jan-01] removed UDP capabilities from skstream
#include <fstream.h>
#include "winsock.h"
//
// sockbuf
//
class sockbuf : public streambuf
{
public:
sockbuf( SOCKET & );
sockbuf( SOCKET &, char *, int );
virtual ~sockbuf();
virtual int overflow(int =EOF) ;
virtual int underflow() ;
virtual int sync();
protected:
char *_buffer ;
// sockbuf specific
SOCKET &_socket ;
} ;
//
// skstream
//
class skstream : public iostream {
public:
// constants
enum service
{
ftp = 21, //tcp
telnet = 23, //tcp
smtp = 25, //tcp mail
time = 37, //tcp timserver
name = 42, //tcp nameserver
nameserver = 53, //tcp domain # name-domain server
finger = 79, //tcp
http = 80, //tcp
pop = 109, //tcp postoffice
pop2 = 109, //tcp # Post Office
pop3 = 110, //tcp postoffice
nntp = 119 //tcp usenet # Network News Transfer
} ;
enum role
{
server ,
client
} ;
// methods
skstream( void );
skstream( const char *addr, const service, const role = client ) ;
skstream( SOCKET );
~skstream( void );
void open( const char *addr, const service, const role = client ) ;
void close( void ) ;
int is_open( void ) const ;
void attach( SOCKET = NULL );
SOCKET getsocket() const ;
char *getpeername( char *, int ) const ;
unsigned short getport( void ) const ;
protected:
SOCKET _socket ;
sockbuf _sockbuf ;
// platform dependent library housekeeping
int init( void ) ;
int shutdown( void ) ;
};
// skstream.cpp
// Copyright (C) 1996, 1995 by John C. Wang <jcwang@csie.ntu.edu.tw>
// All Rights Reserved.
//
// You may distribute this file with your product in either of two ways:
// IN SOURCE CODE FORM: You must include this file in its entirety.
// IN OBJECT CODE FORM: You must give proper acknowledgements to the author(s)
// of this program. This may take the form of credits on the start-up screen.
//
// IN ANYCASE, THIS SOFTWARE IS DISTRIBUTED WITHOUT ANY KIND OF EXPLICIT OR
// IMPLICIT WARRANTY AND THE AUTHOR(S) ARE NOT RESPONSIBLE FOR ANY EVENT THAT
// OCCURS DUE TO THE USE OR MISUSE OF THIS SOFTWARE.
//
// History:
// [JCW 95-Dec-04] created.
// [JCW 95-Dec-20] comments added for distribution 95a.
// [JCW 96-Jan-01] removed UDP capabilities from skstream.
// [JCW 96-Mar-14] exceptions made optional.
// [JCW 96-Oct-20] protected skstream::init and ::shutdown changed.
// char( 255 ) == EOF bug fixed.
#ifdef _UNIX
#define INVALID_SOCKET -1
// Add definitions here
#endif
// Machine independent macros
#ifdef DBG
#define dassert(x) assert(x)
#define debug(x) x
#else
#define dassert(x)
#define debug(x)
#endif //def _DEBUG
//
// skstream
//
int skstream::init( void )
{
// platform dependent initialization
const WORD wMinVer = 0x0101; // request WinSock v1.1 (at least)
WSADATA wsaData;
if( 0 == WSAStartup( wMinVer, &wsaData ) )
return TRUE ;
return FALSE ;
}
int skstream::shutdown( void )
{
// platform dependent shutdown
if( 0 == WSACleanup() )
return TRUE ;
return FALSE ;
}
skstream::skstream( void ) : _sockbuf( _socket ), iostream( &_sockbuf )
{
if( init() )
attach( INVALID_SOCKET ) ;
}
skstream::skstream( const char *addr, const service port, const role side )
: _sockbuf( _socket ), iostream( &_sockbuf )
{
if( init() )
{
attach( INVALID_SOCKET ) ;
open( addr, port, side ) ;
}
}
skstream::skstream( SOCKET sock ) : _sockbuf( _socket ), iostream( &_sockbuf )
{
if( init() )
attach( sock ) ;
}
skstream::~skstream( void )
{
close() ;
shutdown() ;
}
int skstream::is_open( void ) const
{
return ( INVALID_SOCKET != getsocket() ) ;
}
void skstream::open( const char *addr, const service port, const role side )
{
// in case of error condition, user finds out by testing is_open()
if( is_open() )
close() ;
// 1. Create socket
if( INVALID_SOCKET == ( _socket = ::socket( PF_INET, SOCK_STREAM, 0 ) ) )
// Cannot create socket
return ;
// 2. Bind
SOCKADDR_IN sa ;
sa.sin_family = AF_INET ;
sa.sin_addr.S_un.S_addr = INADDR_ANY ;
sa.sin_port = side == client ? 0 : htons( (unsigned short)port ) ;
// rationale: no client requires fixed port number, so let system assign
if( SOCKET_ERROR == ::bind( _socket, (sockaddr *)&sa, sizeof( sa ) ) )
{
// Cannot bind to the chosen port
close() ;
return ;
}
// From now on the two sides are very much different
if( side == skstream::client )
{
// 3(cli). Connect
SOCKADDR_IN sa ;
sa.sin_family = AF_INET ;
hostent *he = NULL;
int ip[4];
if (sscanf( addr, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]) == 4)
{
char ipaddr[4];
for (int i = 0; i < 4; i++)
ipaddr[i] = (char)ip[i];
he = ::gethostbyaddr( ipaddr, 4, PF_INET );
}
else
he = ::gethostbyname( addr );
if( NULL == he )
{
// Cannot resolve remote server ip
close() ;
return ;
}
sa.sin_addr.S_un.S_addr = *(unsigned long *)( he->h_addr_list[ 0 ] ) ;
sa.sin_port = ::htons( port ) ;
if( SOCKET_ERROR == ::connect( _socket, (sockaddr *)&sa, sizeof( sa ) ) )
{
// Connection error
close() ;
return ;
}
}
else
{
// 3(svr). Listen
if( SOCKET_ERROR == ::listen( _socket, 5 ) ) // max backlog
{
// Error listening
close() ;
return ;
}
// 4. Accept
SOCKET commsock ;
if( INVALID_SOCKET == ( commsock = ::accept( _socket, NULL, NULL ) ) )
{
// Accepting error
close() ;
return ;
}
// rationale: after we accept a connection, close the server port
// so another process may become a server
if( SOCKET_ERROR == ::closesocket( _socket ) )
{
// Cannot close service port, resource may be occupied
close() ;
return ;
}
_socket = commsock ;
}
// success!
}
void skstream::close( void )
{
if( is_open() )
{
_sockbuf.sync() ;
if( SOCKET_ERROR == ::closesocket( _socket ) )
// Cannot close. Leave _socket as it was
return ;
}
_socket = INVALID_SOCKET ;
}
void skstream::attach( SOCKET sock )
{
_socket = sock ;
}
SOCKET skstream::getsocket() const
{
return _socket ;
}
char *skstream::getpeername( char *buf, int size ) const
{
SOCKADDR_IN sa ;
int sasize = sizeof( sa ) ;
if( SOCKET_ERROR ==::getpeername( getsocket(), (sockaddr *)&sa, &sasize ) )
// Cannot get peer name
return NULL ;
strncpy( buf, ::inet_ntoa( sa.sin_addr ), size - 1 ) ;
return buf ;
}
unsigned short skstream::getport( void ) const
{
SOCKADDR_IN sa ;
int sasize = sizeof( sa ) ;
if( SOCKET_ERROR ==::getpeername( getsocket(), (sockaddr *)&sa, &sasize ) )
// Cannot get peer port
return ::ntohs( IPPORT_RESERVED ) ;
return ::ntohs( sa.sin_port ) ;
}
//
// sockbuf
//
sockbuf::sockbuf( SOCKET &sock ) : _socket( sock ), streambuf( NULL, 0 )
{
const int insize = 0x2000 ; // allocate 16k buffer each for input and output
const int outsize = 0x2000 ;
const int bufsize = insize + outsize ;
_buffer = new char [ bufsize ] ;
if( this != setbuf( _buffer, bufsize ) )
_buffer = NULL ;
else
{
setp( _buffer, _buffer + insize ) ;
setg( _buffer + insize, _buffer + bufsize, _buffer + bufsize ) ;
}
}
sockbuf::sockbuf( SOCKET &sock, char *buf, int length )
: _socket( sock ), streambuf( buf, length )
{
_buffer = NULL ;
setp( buf, buf + length / 2 ) ;
setg( buf + length / 2, buf + length, buf + length ) ;
}
sockbuf::~sockbuf()
{
delete[] _buffer ;
_buffer = NULL ;
}
int sockbuf::overflow( int nCh )
{
// in case of error, user finds out by testing fail()
if( _socket == INVALID_SOCKET )
// Invalid socket
return EOF ;
if( pptr() - pbase() <= 0 )
// nothing to send
return 0 ;
int size ;
if( SOCKET_ERROR == ( size = ::send( _socket, pbase(), pptr() - pbase(), 0 ) ) )
// (TCP) Cannot send
return EOF ;
if( size == 0 )
// remote site has closed this connection
return EOF ;
if( nCh != EOF ) // size >= 1 at this point
{
size-- ;
*( pbase() + size ) = nCh ;
}
// move remaining pbase() + size .. pptr() - 1 => pbase() .. pptr() - size - 1
for( char *p = pbase() + size; p < pptr(); p++ )
*( p - size ) = *p ;
const int newlen = ( pptr() - pbase() ) - size ;
setp( pbase(), epptr() ) ;
pbump( newlen ) ;
return 0 ;
}
int sockbuf::underflow()
{
// if get area not empty, return first character
// else fill up get area and return 1st character
// in case of error, user finds out by testing eof()
if( _socket == INVALID_SOCKET )
// Invalid socket!
return EOF ;
if( egptr() - gptr() > 0 )
return (int)(unsigned char)(*gptr()) ;
// fill up from eback to egptr
int size ;
if( SOCKET_ERROR == ( size = ::recv( _socket, eback(), egptr() - eback(), 0 ) ) )
// (TCP) Receive error
return EOF ;
if( size == 0 )
// remote site has closed this connection
return EOF ;
// move rcvd data from eback() .. eback() + size to egptr() - size .. egptr()
const int delta = egptr() - ( eback() + size ) ;
for( char *p = eback() + size - 1; p >= eback(); p-- )
{
CHECK( p + delta >= eback(), "Socket underflow" ) ;
CHECK( p + delta < egptr(), "Socket underflow" ) ;
*( p + delta ) = *p ;
}
setg( eback(), egptr() - size, egptr() ) ;
return (int)(unsigned char)(*gptr()) ;
}
int sockbuf::sync()
{
if( EOF == overflow() )
return EOF ; // ios will set the fail bit
else
{
// empty put and get areas
setp( pbase(), epptr() ) ;
setg( eback(), egptr(), egptr() ) ;
return 0 ;
}
}
///////////////////////////////////////////////////////////
// end of skstreams
///////////////////////////////////////////////////////////
#define NO_MFC
#define CObject TObject
#define CString TString
#define CStringArray TString_array
#include "NetSock.h"
#include <windows.h>
static TLanManager* pSocketManager = NULL;
static skstream* cur_socket = NULL;
TSocketClient::TSocketClient()
: m_pData(NULL), m_dwSize(0)
{ }
TSocketClient::~TSocketClient()
{
ReleaseBuffer();
RemoveAllConnections();
ShutDown();
}
BOOL TSocketClient::Boot()
{
return TRUE;
}
BOOL TSocketClient::ShutDown()
{
pSocketManager = NULL;
if (cur_socket)
{
delete cur_socket;
cur_socket = NULL;
}
return TRUE;
}
BOOL TSocketClient::OnRemoveConnection(DWORD id)
{
if (cur_socket)
{
delete cur_socket;
cur_socket = NULL;
}
return TRUE;
}
TConnection* TSocketClient::OnQueryConnection(const char* service,
const char* server)
{
if (pSocketManager == NULL)
Boot();
TConnection* pConnection = NULL;
if (cur_socket == NULL)
{
int porta = service != NULL ? atoi(service) : 0;
TString strServer = server;
int pos = strServer.find("://");
if (pos > 0)
strServer.ltrim(pos+3);
pos = strServer.rfind(':');
if (pos > 0)
{
porta = atoi(strServer.mid(pos+1));
strServer.cut(pos);
}
if (strServer.empty() || stricmp(strServer, "localhost") == 0)
strServer = "127.0.0.1";
cur_socket = new skstream(strServer, (skstream::service)porta);
if (cur_socket->is_open())
pConnection = OnCreateConnection((DWORD)cur_socket);
else
{
delete cur_socket;
cur_socket = NULL;
}
}
return pConnection;
}
BOOL TSocketClient::Execute(DWORD id, const char* cmd)
{
BOOL ok = cur_socket->is_open();
if (ok)
{
const int buflen = strlen(cmd)+1;
cur_socket->sync();
cur_socket->write(cmd, buflen);
cur_socket->flush();
ok = cur_socket->good();
}
return ok;
}
BOOL TSocketClient::Request(DWORD id, const char* cmd)
{
static BOOL semaphore = FALSE;
BOOL ok = FALSE;
if (!semaphore)
{
semaphore = TRUE;
ReleaseBuffer();
ok = Execute(id, cmd);
if (ok)
{
m_dwSize = 0;
cur_socket->read((char*)&m_dwSize, sizeof(m_dwSize));
ok = m_dwSize > 0;
if (ok)
{
m_pData = new BYTE[m_dwSize];
cur_socket->read(m_pData, (int)m_dwSize);
ok = cur_socket->good();
}
}
semaphore = FALSE;
}
return ok;
}
BYTE* TSocketClient::GetBuffer(DWORD& dwSize)
{
dwSize = m_dwSize;
return m_pData;
}
BOOL TSocketClient::WriteLine(DWORD, const char* str)
{
if (!cur_socket->is_open())
return FALSE;
if (str && *str)
{
cur_socket->sync();
cur_socket->write(str, strlen(str));
cur_socket->flush();
}
return cur_socket->good();
}
BOOL TSocketClient::ReadLine(DWORD, TString& str)
{
char *buf = str.get_buffer(4096);
cur_socket->getline(buf, str.size(), '\n');
return TRUE;
}
BOOL TSocketClient::HttpGetFile(const char* remote, const char* local)
{
if (outfile.good())
{
TString msg;
msg << remote << " - ";
if (size > 1024)
msg << (size / 1024) << " K";
else
msg << size << ' ';
msg << "bytes.\nTempo residuo stimato: ";
TProgind pi(size, msg, TRUE, TRUE);
long total = 0;
while (!cur_socket->eof())
{
cur_socket->read(buf.get_buffer(), buf.size());
const int count = cur_socket->gcount();
if (count > 0)
{
outfile.write(buf, count);
total += count;
bool tick = pi.setstatus(total);
if (pi.iscancelled())
break;
if (tick)
{
const double estimated_ticks = double(size - total) * double(clock() - start) / total;
long secs = long(estimated_ticks / CLOCKS_PER_SEC) + 1;
CHECK(secs >= 0, "Bad time estimation");
const int hours = int(secs / 3600L);
secs %= 3600L;
const int mins = int(secs / 60L);
secs %= 60L;
const int append_pos = msg.find("o: ")+3;
msg.cut(append_pos);
TString16 tempo;
tempo.format("%02d:%02d:%02ld", hours, mins, secs);
msg << tempo;
pi.set_text(msg);
}
}
}
ok = pi.isfinished();
}
else
error_box("Impossibile scrivere il file %s", local);
}
return ok;
}
BOOL TSocketClient::HttpGetDir(const char* remote, TString_array& list)
{
TFilename local; local.temp();
const BOOL ok = HttpGetFile(remote, local);
if (ok)
{
ifstream s(local);
TString riga;
while (!s.eof())
{
s.getline(riga.get_buffer(), riga.size());
const int href = riga.find("HREF=");
if (href > 0)
{
const int start = riga.find('"', href) + 1;
if (start > href)
{
const int stop = riga.find('"', start);
if (stop > start)
{
const TString& name = riga.sub(start, stop);
list.add(name);
}
}
}
}
s.close();
::remove(local);
}
return ok;
}
void TSocketClient::ReleaseBuffer()
{
if (m_pData)
{
delete m_pData;
m_pData = NULL;
m_dwSize = 0;
}
}