
#include "sockimpl.h" #include <stdarg.h> using namespace etwork; using namespace etwork::impl; #if !defined( NDEBUG ) bool etwork::impl::gDebugging = true; #else bool etwork::impl::gDebugging = false; #endif bool etwork::impl::wsOpen = false; Lock etwork::impl::gethostLock; //!< gethostbyname is not thread safe IErrorNotify * etwork::impl::gErrorNotify; void SetEtworkErrorNotify( IErrorNotify * notify ) { gErrorNotify = notify; } static EtworkError GetWsaError( int wsaError, ErrorArea area ) { ErrorOption eo = EO_unknown_error; ErrorSeverity es = ES_error; switch( wsaError ) { case WSAEINTR: break; case WSAEACCES: eo = EO_already_in_use; break; case WSAEFAULT: eo = EO_invalid_parameters; break; case WSAEINVAL: eo = EO_invalid_parameters; break; case WSAEMFILE: eo = EO_out_of_resources; es = ES_catastrophe; break; case WSAEWOULDBLOCK: es = ES_warning; break; case WSAEINPROGRESS: es = ES_warning; break; case WSAEALREADY: eo = EO_invalid_parameters; break; case WSAENOTSOCK: eo = EO_invalid_parameters; break; case WSAEDESTADDRREQ: eo = EO_invalid_parameters; es = ES_catastrophe; break; case WSAEMSGSIZE: eo = EO_out_of_resources; es = ES_warning; break; case WSAEPROTOTYPE: eo = EO_invalid_parameters; break; case WSAENOPROTOOPT: eo = EO_invalid_parameters; break; case WSAEPROTONOSUPPORT: eo = EO_unsupported_platform; es = ES_catastrophe; break; case WSAESOCKTNOSUPPORT: eo = EO_unsupported_platform; es = ES_catastrophe; break; case WSAEOPNOTSUPP: eo = EO_invalid_parameters; break; case WSAEPFNOSUPPORT: eo = EO_unsupported_platform; es = ES_catastrophe; break; case WSAEAFNOSUPPORT: eo = EO_unsupported_platform; es = ES_catastrophe; break; case WSAEADDRINUSE: eo = EO_already_in_use; break; case WSAEADDRNOTAVAIL: eo = EO_invalid_parameters; break; case WSAENETDOWN: eo = EO_out_of_resources; break; case WSAENETUNREACH: eo = EO_bad_address; break; case WSAENETRESET: eo = EO_out_of_resources; break; case WSAECONNABORTED: eo = EO_peer_timeout; break; case WSAECONNRESET: eo = EO_peer_dropped; break; case WSAENOBUFS: eo = EO_out_of_resources; break; case WSAEISCONN: eo = EO_invalid_parameters; es = ES_catastrophe; break; case WSAENOTCONN: eo = EO_invalid_parameters; es = ES_catastrophe; break; case WSAESHUTDOWN: eo = EO_invalid_parameters; break; case WSAETIMEDOUT: eo = EO_peer_timeout; break; case WSAECONNREFUSED: eo = EO_peer_refused; break; case WSAEHOSTDOWN: eo = EO_peer_timeout; break; case WSAEHOSTUNREACH: eo = EO_bad_address; break; case WSAEPROCLIM: eo = EO_out_of_resources; es = ES_catastrophe; break; case WSASYSNOTREADY: eo = EO_unsupported_platform; es = ES_catastrophe; break; case WSAVERNOTSUPPORTED: eo = EO_unsupported_version; es = ES_catastrophe; break; case WSANOTINITIALISED: es = ES_catastrophe; break; case WSAEDISCON: eo = EO_peer_dropped; break; case WSATYPE_NOT_FOUND: break; case WSAHOST_NOT_FOUND: eo = EO_bad_address; break; case WSATRY_AGAIN: eo = EO_bad_address; break; case WSANO_RECOVERY: eo = EO_bad_address; break; case WSANO_DATA: eo = EO_bad_address; break; case WSA_INVALID_HANDLE: eo = EO_invalid_parameters; es = ES_catastrophe; break; case WSA_INVALID_PARAMETER: eo = EO_invalid_parameters; es = ES_catastrophe; break; case WSA_IO_INCOMPLETE: break; case WSA_IO_PENDING: break; case WSA_NOT_ENOUGH_MEMORY: eo = EO_out_of_resources; break; case WSA_OPERATION_ABORTED: eo = EO_peer_dropped; break; case WSASYSCALLFAILURE: break; default: { char buf[1024]; _snprintf( buf, 1024, "Unknown system error code in GetWsaError(): %d\n", wsaError ); buf[1023] = 0; OutputDebugString( buf ); } break; } return EtworkError( es, area, eo ); } std::string etwork::impl::get_error_string( int err ) { std::string error_; if( err == 0 ) { error_ = ""; } else { char buf[2048]; int l = ::FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, buf, 2048, 0 ); if( l < 0 || l > 2047 ) { l = 2047; } buf[l] = 0; error_ = buf; size_t pos; while( (pos = error_.find( '\n' )) != std::string::npos ) { error_.erase( pos, 1 ); } while( (pos = error_.find( '\r' )) != std::string::npos ) { error_.erase( pos, 1 ); } while( (pos = error_.find( '.' )) != std::string::npos ) { error_.erase( pos, 1 ); } } return error_; } void etwork::impl::assert_failure( char const * expr, char const * file, int line ) { char str[2048]; _snprintf( str, 2048, "Etwork: %s(%d): Assertion Failed: %s\n", file, line, expr ); str[2047] = 0; if( gErrorNotify ) { ErrorInfo info; info.error = EtworkError( ES_internal, EA_unknown, EO_unknown_error ); info.osError = -1; info.socket = 0; info.error.setText( str ); gErrorNotify->onSocketError( info ); } else { OutputDebugString( str ); if( gDebugging ) { if( ::MessageBox( NULL, str, "Etwork: Assert Failure", MB_OKCANCEL | MB_ICONSTOP ) == IDCANCEL ) { OutputDebugString( "User pressed CANCEL on assert dialog; hitting breakpoint.\n" ); __asm { int 3 } } } } } EtworkError::EtworkError() { error_ = 0; text_ = 0; } EtworkError::EtworkError( int severity, int area, int option ) { ASSERT( (severity&ES_mask) == severity ); ASSERT( (area&EA_mask) == area ); ASSERT( (option&EO_mask) == option ); error_ = severity | area | option; text_ = 0; } EtworkError::EtworkError( EtworkError const & o ) { error_ = o.error_; if( o.text_ ) { text_ = ::strdup( o.text_ ); } else { text_ = 0; } } EtworkError::~EtworkError() { ::free( text_ ); } EtworkError & EtworkError::operator=( EtworkError const & o ) { if( text_ ) { ::free( text_ ); } new( (void *)this ) EtworkError( o ); return *this; } EtworkError & EtworkError::operator=( int err ) { error_ = err; return *this; } EtworkError::operator int() const { return error_; } int EtworkError::severity() const { return error_ & ES_mask; } int EtworkError::area() const { return error_ & EA_mask; } int EtworkError::option() const { return error_ & EO_mask; } bool EtworkError::operator==( EtworkError const & o ) const { return error_ == o.error_; } bool EtworkError::operator!() const { return !error_; } EtworkError::operator bool() const { return !!error_; } static char const * severity_str( int sev ) { switch( sev & ES_mask ) { case ES_internal: return "internal error"; case ES_catastrophe: return "catastrophic error"; case ES_error: return "runtime error"; case ES_warning: return "runtime warning"; case ES_note: return "runtime note"; default: return "illegal severity code"; } } static char const * area_str( int area ) { switch( area & EA_mask ) { case EA_unknown: return "unknown area"; case EA_dispose: return "dispose"; case EA_session: return "session"; case EA_buffer: return "buffer"; case EA_connect: return "connection"; case EA_address: return "address"; case EA_init: return "initialization"; default: return "illegal area code"; } } static char const * option_str( int option ) { switch( option & EO_mask ) { case EO_no_error: return "no error"; case EO_unknown_error: return "unknown error"; case EO_unsupported_version: return "unsupported version"; case EO_unsupported_platform: return "unsupported platform"; case EO_invalid_parameters: return "invalid parameters"; case EO_buffer_full: return "buffer full"; case EO_out_of_resources: return "out of resources"; case EO_bad_address: return "bad address"; case EO_already_in_use: return "already in use"; case EO_peer_refused: return "peer refused connection"; case EO_peer_dropped: return "peer dropped connection"; case EO_peer_timeout: return "peer timed out"; case EO_peer_violation: return "peer violated protocol"; default: return "illegal option code"; } } char const * EtworkError::c_str() const { if( !text_ ) { char buf[2048]; _snprintf( buf, 2048, "%s in %s: %s", severity_str( severity() ), area_str( area() ), option_str( option() ) ); buf[2047] = 0; text_ = ::strdup( buf ); } return text_; } void EtworkError::setText( char const * text ) { if( text_ ) { ::free( text_ ); } if( text ) { text_ = ::strdup( text ); } else { text_ = 0; } } bool etwork::impl::wsa_error( int wsaErr, ErrorArea area ) { return wsa_error_from( (ISocket *)0, (ISocketManager *)0, wsaErr, area ); } bool etwork::impl::wsa_error_from( ISocket * sock, int wsaErr, ErrorArea area ) { return wsa_error_from( sock, sock ? static_cast< Socket * >( sock )->mgr_ : 0, wsaErr, area ); } bool etwork::impl::wsa_error_from( ISocket * sock, ISocketManager * mgr, int wsaErr, ErrorArea area ) { IErrorNotify * en = gErrorNotify; if( mgr ) { SocketManager * smgr = static_cast< SocketManager *> ( mgr ); if( smgr->settings_.notify ) { en = smgr->settings_.notify; } } SocketManager * sm = static_cast< SocketManager * >( mgr ); ErrorInfo info; info.error = GetWsaError( wsaErr, area ); info.osError = wsaErr; info.socket = sock; if( en ) { en->onSocketError( info ); } else if( info.error.severity() >= ES_error || (sm && sm->settings_.debug) || gDebugging ) { char buf[2048]; _snprintf( buf, 2048, "Etwork: Error %d in wsa_error_from(): %s (%s)\n", wsaErr, info.error.c_str(), get_error_string( wsaErr ).c_str() ); buf[2047] = 0; OutputDebugString( buf ); } return info.error.severity() < ES_catastrophe; } bool etwork::impl::etwork_error_from( ISocket * sock, ISocketManager * mgr, EtworkError err ) { IErrorNotify * en = gErrorNotify; if( mgr ) { SocketManager * smgr = static_cast< SocketManager *> ( mgr ); if( smgr->settings_.notify ) { en = smgr->settings_.notify; } } SocketManager * sm = static_cast< SocketManager * >( mgr ); ErrorInfo info; info.error = err; info.osError = 0; info.socket = sock; if( en ) { en->onSocketError( info ); } else if( (sm && sm->settings_.debug) || gDebugging ) { char buf[2048]; _snprintf( buf, 2048, "Etwork: Error in etwork_error_from(): %s\n", info.error.c_str() ); buf[2047] = 0; OutputDebugString( buf ); } return info.error.severity() < ES_catastrophe; } bool etwork::impl::etwork_info_from( ISocketManager * mgr, ErrorInfo err ) { IErrorNotify * en = gErrorNotify; if( mgr ) { SocketManager * smgr = static_cast< SocketManager *> ( mgr ); if( smgr->settings_.notify ) { en = smgr->settings_.notify; } } SocketManager * sm = static_cast< SocketManager * >( mgr ); if( err.socket ) { sm = static_cast< SocketManager * >( static_cast< Socket * >( err.socket )->mgr_ ); if( sm->settings_.notify ) { en = sm->settings_.notify; } } if( en ) { en->onSocketError( err ); } else if( (sm && sm->settings_.debug) || gDebugging ) { char buf[2048]; _snprintf( buf, 2048, "Etwork: Error in etwork_error_from(): %s (%s)\n", err.error.c_str(), get_error_string( err.osError ).c_str() ); buf[2047] = 0; OutputDebugString( buf ); } return err.error.severity() < ES_catastrophe; } void etwork::impl::etwork_log( ISocket * sock, ErrorSeverity sev, char const * text, ... ) { char buf[2048]; va_list vl; va_start( vl, text ); _vsnprintf( buf, 2048, text, vl ); va_end( vl ); buf[2047] = 0; ErrorInfo ei; ei.error = EtworkError( sev, EA_unknown, EO_no_error ); ei.error.setText( buf ); ei.osError = 0; ei.socket = 0; etwork_info_from( 0, ei ); }