mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
3564 lines
88 KiB
C++
3564 lines
88 KiB
C++
/** @file
|
|
*
|
|
* Copyright (c) 1999-2012 Jim Hull <csocket@jimloco.com>
|
|
* All rights reserved
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright notice, this list
|
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
* Redistributions in any form must be accompanied by information on how to obtain
|
|
* complete source code for this software and any accompanying software that uses this software.
|
|
* The source code must either be included in the distribution or be available for no more than
|
|
* the cost of distribution plus a nominal fee, and must be freely redistributable
|
|
* under reasonable conditions. For an executable file, complete source code means the source
|
|
* code for all modules it contains. It does not include source code for modules or files
|
|
* that typically accompany the major components of the operating system on which the executable file runs.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
|
* OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OF THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*
|
|
*/
|
|
|
|
/***
|
|
* doing this because there seems to be a bug that is losing the "short" on htons when in optimize mode turns into a macro
|
|
* gcc 4.3.4
|
|
*/
|
|
#if defined(__OPTIMIZE__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 3
|
|
#pragma GCC diagnostic warning "-Wconversion"
|
|
#endif /* defined(__OPTIMIZE__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 3 */
|
|
|
|
#include <znc/Csocket.h>
|
|
#ifdef __NetBSD__
|
|
#include <sys/param.h>
|
|
#endif /* __NetBSD__ */
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
#include <stdio.h>
|
|
#include <openssl/conf.h>
|
|
#include <openssl/engine.h>
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
|
|
|
|
#include <list>
|
|
|
|
#define CS_SRANDBUFFER 128
|
|
|
|
using namespace std;
|
|
|
|
#define CREATE_ARES_VER( a, b, c ) ((a<<16)|(b<<8)|c)
|
|
|
|
#ifndef _NO_CSOCKET_NS // some people may not want to use a namespace
|
|
namespace Csocket
|
|
{
|
|
#endif /* _NO_CSOCKET_NS */
|
|
|
|
static int g_iCsockSSLIdx = 0; //!< this get setup once in InitSSL
|
|
int GetCsockClassIdx()
|
|
{
|
|
return( g_iCsockSSLIdx );
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
#if defined(_WIN32) && (!defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600))
|
|
//! thanks to KiNgMaR @ #znc for this wrapper
|
|
static int inet_pton( int af, const char *src, void *dst )
|
|
{
|
|
sockaddr_storage aAddress;
|
|
int iAddrLen = sizeof( sockaddr_storage );
|
|
memset( &aAddress, 0, iAddrLen );
|
|
char *pTmp = strdup( src );
|
|
aAddress.ss_family = af; // this is important:
|
|
// The function fails if the sin_family member of the SOCKADDR_IN structure is not set to AF_INET or AF_INET6.
|
|
int iRet = WSAStringToAddressA( pTmp, af, NULL, ( sockaddr * )&aAddress, &iAddrLen );
|
|
free( pTmp );
|
|
if( iRet == 0 )
|
|
{
|
|
if( af == AF_INET6 )
|
|
memcpy( dst, &(( sockaddr_in6 * )&aAddress )->sin6_addr, sizeof( in6_addr ) );
|
|
else
|
|
memcpy( dst, &(( sockaddr_in * )&aAddress )->sin_addr, sizeof( in_addr ) );
|
|
return( 1 );
|
|
}
|
|
return( -1 );
|
|
}
|
|
#endif
|
|
|
|
static inline void set_non_blocking( cs_sock_t fd )
|
|
{
|
|
u_long iOpts = 1;
|
|
ioctlsocket( fd, FIONBIO, &iOpts );
|
|
}
|
|
|
|
/*
|
|
* not used by anything anymore
|
|
static inline void set_blocking(cs_sock_t fd)
|
|
{
|
|
u_long iOpts = 0;
|
|
ioctlsocket( fd, FIONBIO, &iOpts );
|
|
}
|
|
*/
|
|
|
|
static inline void set_close_on_exec( cs_sock_t fd )
|
|
{
|
|
// TODO add this for windows
|
|
// see http://gcc.gnu.org/ml/java-patches/2002-q1/msg00696.html
|
|
// for infos on how to do this
|
|
}
|
|
#else
|
|
static inline void set_non_blocking( cs_sock_t fd )
|
|
{
|
|
int fdflags = fcntl( fd, F_GETFL, 0 );
|
|
if( fdflags < 0 )
|
|
return; // Ignore errors
|
|
fcntl( fd, F_SETFL, fdflags|O_NONBLOCK );
|
|
}
|
|
|
|
/*
|
|
* not used by anything anymore
|
|
static inline void set_blocking(cs_sock_t fd)
|
|
{
|
|
int fdflags = fcntl(fd, F_GETFL, 0);
|
|
if ( fdflags < 0 )
|
|
return; // Ignore errors
|
|
fdflags &= ~O_NONBLOCK;
|
|
fcntl( fd, F_SETFL, fdflags );
|
|
}
|
|
*/
|
|
|
|
static inline void set_close_on_exec( cs_sock_t fd )
|
|
{
|
|
int fdflags = fcntl( fd, F_GETFD, 0 );
|
|
if( fdflags < 0 )
|
|
return; // Ignore errors
|
|
fcntl( fd, F_SETFD, fdflags|FD_CLOEXEC );
|
|
}
|
|
#endif /* _WIN32 */
|
|
|
|
void CSSockAddr::SinFamily()
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
m_saddr6.sin6_family = PF_INET6;
|
|
#endif /* HAVE_IPV6 */
|
|
m_saddr.sin_family = PF_INET;
|
|
}
|
|
void CSSockAddr::SinPort( u_short iPort )
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
m_saddr6.sin6_port = htons( iPort );
|
|
#endif /* HAVE_IPV6 */
|
|
m_saddr.sin_port = htons( iPort );
|
|
}
|
|
|
|
void CSSockAddr::SetIPv6( bool b )
|
|
{
|
|
#ifndef HAVE_IPV6
|
|
if( b )
|
|
{
|
|
CS_DEBUG( "-DHAVE_IPV6 must be set during compile time to enable this feature" );
|
|
m_bIsIPv6 = false;
|
|
return;
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
m_bIsIPv6 = b;
|
|
SinFamily();
|
|
}
|
|
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
Csock *GetCsockFromCTX( X509_STORE_CTX *pCTX )
|
|
{
|
|
Csock *pSock = NULL;
|
|
SSL *pSSL = ( SSL * )X509_STORE_CTX_get_ex_data( pCTX, SSL_get_ex_data_X509_STORE_CTX_idx() );
|
|
if( pSSL )
|
|
pSock = ( Csock * )SSL_get_ex_data( pSSL, GetCsockClassIdx() );
|
|
return( pSock );
|
|
}
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
|
|
#ifdef USE_GETHOSTBYNAME
|
|
|
|
// this issue here is getaddrinfo has a significant behavior difference when dealing with round robin dns on an
|
|
// ipv4 network. This is not desirable IMHO. so when this is compiled without ipv6 support backwards compatibility
|
|
// is maintained.
|
|
|
|
static int __GetHostByName( const CS_STRING & sHostName, struct in_addr *paddr, u_int iNumRetries )
|
|
{
|
|
int iReturn = HOST_NOT_FOUND;
|
|
struct hostent *hent = NULL;
|
|
#ifdef __linux__
|
|
char hbuff[2048];
|
|
struct hostent hentbuff;
|
|
|
|
int err;
|
|
for( u_int a = 0; a < iNumRetries; a++ )
|
|
{
|
|
memset(( char * )hbuff, '\0', 2048 );
|
|
iReturn = gethostbyname_r( sHostName.c_str(), &hentbuff, hbuff, 2048, &hent, &err );
|
|
|
|
if( iReturn == 0 )
|
|
break;
|
|
|
|
if( iReturn != TRY_AGAIN )
|
|
{
|
|
CS_DEBUG( "gethostyname_r: " << hstrerror( h_errno ) );
|
|
break;
|
|
}
|
|
|
|
}
|
|
if( !hent && iReturn == 0 )
|
|
iReturn = HOST_NOT_FOUND;
|
|
#else
|
|
for( u_int a = 0; a < iNumRetries; a++ )
|
|
{
|
|
iReturn = HOST_NOT_FOUND;
|
|
hent = gethostbyname( sHostName.c_str() );
|
|
|
|
if( hent )
|
|
{
|
|
iReturn = 0;
|
|
break;
|
|
}
|
|
|
|
if( h_errno != TRY_AGAIN )
|
|
{
|
|
#ifndef _WIN32
|
|
CS_DEBUG( "gethostyname: " << hstrerror( h_errno ) );
|
|
#endif /* _WIN32 */
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif /* __linux__ */
|
|
|
|
if( iReturn == 0 )
|
|
memcpy( &paddr->s_addr, hent->h_addr_list[0], sizeof( paddr->s_addr ) );
|
|
|
|
return( iReturn == TRY_AGAIN ? EAGAIN : iReturn );
|
|
}
|
|
#endif /* !USE_GETHOSTBYNAME */
|
|
|
|
#ifdef HAVE_C_ARES
|
|
void Csock::FreeAres()
|
|
{
|
|
if( m_pARESChannel )
|
|
{
|
|
ares_destroy( m_pARESChannel );
|
|
m_pARESChannel = NULL;
|
|
}
|
|
}
|
|
#endif /* HAVE_C_ARES */
|
|
|
|
#ifdef HAVE_C_ARES
|
|
static void AresHostCallback( void *pArg, int status, int timeouts, struct hostent *hent )
|
|
{
|
|
Csock *pSock = ( Csock * )pArg;
|
|
if( status == ARES_SUCCESS && hent && hent->h_addr_list[0] != NULL )
|
|
{
|
|
CSSockAddr *pSockAddr = pSock->GetCurrentAddr();
|
|
if( hent->h_addrtype == AF_INET )
|
|
{
|
|
pSock->SetIPv6( false );
|
|
memcpy( pSockAddr->GetAddr(), hent->h_addr_list[0], sizeof( *( pSockAddr->GetAddr() ) ) );
|
|
}
|
|
#ifdef HAVE_IPV6
|
|
else if( hent->h_addrtype == AF_INET6 )
|
|
{
|
|
pSock->SetIPv6( true );
|
|
memcpy( pSockAddr->GetAddr6(), hent->h_addr_list[0], sizeof( *( pSockAddr->GetAddr6() ) ) );
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
else
|
|
{
|
|
status = ARES_ENOTFOUND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CS_DEBUG( ares_strerror( status ) );
|
|
if( status == ARES_SUCCESS )
|
|
{
|
|
CS_DEBUG( "Received ARES_SUCCESS without any useful reply, using NODATA instead" );
|
|
status = ARES_ENODATA;
|
|
}
|
|
}
|
|
pSock->SetAresFinished( status );
|
|
}
|
|
#endif /* HAVE_C_ARES */
|
|
|
|
CGetAddrInfo::CGetAddrInfo( const CS_STRING & sHostname, Csock *pSock, CSSockAddr & csSockAddr )
|
|
: m_pSock( pSock ), m_csSockAddr( csSockAddr )
|
|
{
|
|
m_sHostname = sHostname;
|
|
m_pAddrRes = NULL;
|
|
m_iRet = ETIMEDOUT;
|
|
}
|
|
CGetAddrInfo::~CGetAddrInfo()
|
|
{
|
|
if( m_pAddrRes )
|
|
freeaddrinfo( m_pAddrRes );
|
|
m_pAddrRes = NULL;
|
|
}
|
|
|
|
void CGetAddrInfo::Init()
|
|
{
|
|
memset(( struct addrinfo * )&m_cHints, '\0', sizeof( m_cHints ) );
|
|
m_cHints.ai_family = m_csSockAddr.GetAFRequire();
|
|
|
|
m_cHints.ai_socktype = SOCK_STREAM;
|
|
m_cHints.ai_protocol = IPPROTO_TCP;
|
|
#ifdef AI_ADDRCONFIG
|
|
// this is suppose to eliminate host from appearing that this system can not support
|
|
m_cHints.ai_flags = AI_ADDRCONFIG;
|
|
#endif /* AI_ADDRCONFIG */
|
|
|
|
if( m_pSock && ( m_pSock->GetType() == Csock::LISTENER || m_pSock->GetConState() == Csock::CST_BINDVHOST ) )
|
|
{
|
|
// when doing a dns for bind only, set the AI_PASSIVE flag as suggested by the man page
|
|
m_cHints.ai_flags |= AI_PASSIVE;
|
|
}
|
|
}
|
|
|
|
int CGetAddrInfo::Process()
|
|
{
|
|
m_iRet = getaddrinfo( m_sHostname.c_str(), NULL, &m_cHints, &m_pAddrRes );
|
|
if( m_iRet == EAI_AGAIN )
|
|
return( EAGAIN );
|
|
else if( m_iRet == 0 )
|
|
return( 0 );
|
|
return( ETIMEDOUT );
|
|
}
|
|
|
|
int CGetAddrInfo::Finish()
|
|
{
|
|
if( m_iRet == 0 && m_pAddrRes )
|
|
{
|
|
std::list<struct addrinfo *> lpTryAddrs;
|
|
bool bFound = false;
|
|
for( struct addrinfo *pRes = m_pAddrRes; pRes; pRes = pRes->ai_next )
|
|
{
|
|
// pass through the list building out a lean list of candidates to try. AI_CONFIGADDR doesn't always seem to work
|
|
#ifdef __sun
|
|
if(( pRes->ai_socktype != SOCK_STREAM ) || ( pRes->ai_protocol != IPPROTO_TCP && pRes->ai_protocol != IPPROTO_IP ) )
|
|
#else
|
|
if(( pRes->ai_socktype != SOCK_STREAM ) || ( pRes->ai_protocol != IPPROTO_TCP ) )
|
|
#endif /* __sun work around broken impl of getaddrinfo */
|
|
continue;
|
|
|
|
if(( m_csSockAddr.GetAFRequire() != CSSockAddr::RAF_ANY ) && ( pRes->ai_family != m_csSockAddr.GetAFRequire() ) )
|
|
continue; // they requested a special type, so be certain we woop past anything unwanted
|
|
lpTryAddrs.push_back( pRes );
|
|
}
|
|
for( std::list<struct addrinfo *>::iterator it = lpTryAddrs.begin(); it != lpTryAddrs.end(); )
|
|
{
|
|
// cycle through these, leaving the last iterator for the outside caller to call, so if there is an error it can call the events
|
|
struct addrinfo * pRes = *it;
|
|
bool bTryConnect = false;
|
|
if( pRes->ai_family == AF_INET )
|
|
{
|
|
if( m_pSock )
|
|
m_pSock->SetIPv6( false );
|
|
m_csSockAddr.SetIPv6( false );
|
|
struct sockaddr_in *pTmp = ( struct sockaddr_in * )pRes->ai_addr;
|
|
memcpy( m_csSockAddr.GetAddr(), &( pTmp->sin_addr ), sizeof( *( m_csSockAddr.GetAddr() ) ) );
|
|
if( m_pSock && m_pSock->GetConState() == Csock::CST_DESTDNS && m_pSock->GetType() == Csock::OUTBOUND )
|
|
{
|
|
bTryConnect = true;
|
|
}
|
|
else
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef HAVE_IPV6
|
|
else if( pRes->ai_family == AF_INET6 )
|
|
{
|
|
if( m_pSock )
|
|
m_pSock->SetIPv6( true );
|
|
m_csSockAddr.SetIPv6( true );
|
|
struct sockaddr_in6 *pTmp = ( struct sockaddr_in6 * )pRes->ai_addr;
|
|
memcpy( m_csSockAddr.GetAddr6(), &( pTmp->sin6_addr ), sizeof( *( m_csSockAddr.GetAddr6() ) ) );
|
|
if( m_pSock && m_pSock->GetConState() == Csock::CST_DESTDNS && m_pSock->GetType() == Csock::OUTBOUND )
|
|
{
|
|
bTryConnect = true;
|
|
}
|
|
else
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
it++; // increment the iterator her so we know if its the last element or not
|
|
|
|
if( bTryConnect && it != lpTryAddrs.end() )
|
|
{
|
|
// save the last attempt for the outer loop, the issue then becomes that the error is thrown on the last failure
|
|
if( m_pSock->CreateSocksFD() && m_pSock->Connect() )
|
|
{
|
|
m_pSock->SetSkipConnect( true ); // this tells the socket that the connection state has been started
|
|
bFound = true;
|
|
break;
|
|
}
|
|
m_pSock->CloseSocksFD();
|
|
}
|
|
else if( bTryConnect )
|
|
{
|
|
bFound = true;
|
|
}
|
|
}
|
|
|
|
if( bFound ) // the data pointed to here is invalid now, but the pointer itself is a good test
|
|
{
|
|
return( 0 );
|
|
}
|
|
}
|
|
return( ETIMEDOUT );
|
|
}
|
|
|
|
int GetAddrInfo( const CS_STRING & sHostname, Csock *pSock, CSSockAddr & csSockAddr )
|
|
{
|
|
#ifdef USE_GETHOSTBYNAME
|
|
if( pSock )
|
|
pSock->SetIPv6( false );
|
|
csSockAddr.SetIPv6( false );
|
|
int iRet = __GetHostByName( sHostname, csSockAddr.GetAddr(), 3 );
|
|
return( iRet );
|
|
#else
|
|
CGetAddrInfo cInfo( sHostname, pSock, csSockAddr );
|
|
cInfo.Init();
|
|
int iRet = cInfo.Process();
|
|
if( iRet != 0 )
|
|
return( iRet );
|
|
return( cInfo.Finish() );
|
|
#endif /* USE_GETHOSTBYNAME */
|
|
}
|
|
|
|
int Csock::ConvertAddress( const struct sockaddr_storage * pAddr, socklen_t iAddrLen, CS_STRING & sIP, u_short * piPort )
|
|
{
|
|
char szHostname[NI_MAXHOST];
|
|
char szServ[NI_MAXSERV];
|
|
int iRet = getnameinfo(( const struct sockaddr * )pAddr, iAddrLen, szHostname, NI_MAXHOST, szServ, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV );
|
|
if( iRet == 0 )
|
|
{
|
|
sIP = szHostname;
|
|
if( piPort )
|
|
*piPort = ( u_short )atoi( szServ );
|
|
}
|
|
return( iRet );
|
|
}
|
|
|
|
bool InitCsocket()
|
|
{
|
|
#ifdef _WIN32
|
|
WSADATA wsaData;
|
|
int iResult = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
|
|
if( iResult != NO_ERROR )
|
|
return( false );
|
|
#endif /* _WIN32 */
|
|
#ifdef HAVE_C_ARES
|
|
#if ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 )
|
|
if( ares_library_init( ARES_LIB_INIT_ALL ) != 0 )
|
|
return( false );
|
|
#endif /* ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 ) */
|
|
#endif /* HAVE_C_ARES */
|
|
#ifdef HAVE_LIBSSL
|
|
if( !InitSSL() )
|
|
return( false );
|
|
#endif /* HAVE_LIBSSL */
|
|
return( true );
|
|
}
|
|
|
|
void ShutdownCsocket()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
ERR_remove_state( 0 );
|
|
ENGINE_cleanup();
|
|
CONF_modules_unload( 1 );
|
|
ERR_free_strings();
|
|
EVP_cleanup();
|
|
CRYPTO_cleanup_all_ex_data();
|
|
#endif /* HAVE_LIBSSL */
|
|
#ifdef HAVE_C_ARES
|
|
#if ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 )
|
|
ares_library_cleanup();
|
|
#endif /* ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 ) */
|
|
#endif /* HAVE_C_ARES */
|
|
#ifdef _WIN32
|
|
WSACleanup();
|
|
#endif /* _WIN32 */
|
|
}
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
bool InitSSL( ECompType eCompressionType )
|
|
{
|
|
SSL_load_error_strings();
|
|
if( SSL_library_init() != 1 )
|
|
{
|
|
CS_DEBUG( "SSL_library_init() failed!" );
|
|
return( false );
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
if( access( "/dev/urandom", R_OK ) == 0 )
|
|
RAND_load_file( "/dev/urandom", 1024 );
|
|
else if( access( "/dev/random", R_OK ) == 0 )
|
|
RAND_load_file( "/dev/random", 1024 );
|
|
else
|
|
{
|
|
CS_DEBUG( "Unable to locate entropy location! Tried /dev/urandom and /dev/random" );
|
|
return( false );
|
|
}
|
|
#endif /* _WIN32 */
|
|
|
|
COMP_METHOD *cm = NULL;
|
|
|
|
if( CT_ZLIB & eCompressionType )
|
|
{
|
|
cm = COMP_zlib();
|
|
if( cm )
|
|
SSL_COMP_add_compression_method( CT_ZLIB, cm );
|
|
}
|
|
|
|
if( CT_RLE & eCompressionType )
|
|
{
|
|
cm = COMP_rle();
|
|
if( cm )
|
|
SSL_COMP_add_compression_method( CT_RLE, cm );
|
|
}
|
|
|
|
// setting this up once in the begining
|
|
g_iCsockSSLIdx = SSL_get_ex_new_index( 0, ( void * )"CsockGlobalIndex", NULL, NULL, NULL );
|
|
|
|
return( true );
|
|
}
|
|
|
|
void SSLErrors( const char *filename, u_int iLineNum )
|
|
{
|
|
unsigned long iSSLError = 0;
|
|
while(( iSSLError = ERR_get_error() ) != 0 )
|
|
{
|
|
CS_DEBUG( "at " << filename << ":" << iLineNum );
|
|
char szError[512];
|
|
memset(( char * ) szError, '\0', 512 );
|
|
ERR_error_string_n( iSSLError, szError, 511 );
|
|
if( *szError )
|
|
CS_DEBUG( szError );
|
|
}
|
|
}
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
void CSAdjustTVTimeout( struct timeval & tv, long iTimeoutMS )
|
|
{
|
|
if( iTimeoutMS >= 0 )
|
|
{
|
|
long iCurTimeout = tv.tv_usec / 1000;
|
|
iCurTimeout += tv.tv_sec * 1000;
|
|
if( iCurTimeout > iTimeoutMS )
|
|
{
|
|
tv.tv_sec = iTimeoutMS / 1000;
|
|
tv.tv_usec = iTimeoutMS % 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define CS_UNKNOWN_ERROR "Unknown Error"
|
|
static const char * CS_StrError( int iErrno, char * pszBuff, size_t uBuffLen )
|
|
{
|
|
#if defined( sgi ) || defined(__sun) || defined(_WIN32) || (defined(__NetBSD_Version__) && __NetBSD_Version__ < 4000000000)
|
|
return( strerror( iErrno ) );
|
|
#else
|
|
memset( pszBuff, '\0', uBuffLen );
|
|
#if !defined( _GNU_SOURCE )
|
|
if( strerror_r( iErrno, pszBuff, uBuffLen ) == 0 )
|
|
return( pszBuff );
|
|
#else
|
|
return( strerror_r( iErrno, pszBuff, uBuffLen ) );
|
|
#endif /* (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !defined( _GNU_SOURCE ) */
|
|
#endif /* defined( sgi ) || defined(__sun) || defined(_WIN32) || (defined(__NetBSD_Version__) && __NetBSD_Version__ < 4000000000) */
|
|
return( CS_UNKNOWN_ERROR );
|
|
}
|
|
|
|
void __Perror( const CS_STRING & s, const char *pszFile, unsigned int iLineNo )
|
|
{
|
|
char szBuff[0xff];
|
|
std::cerr << s << "(" << pszFile << ":" << iLineNo << "): " << CS_StrError( GetSockError(), szBuff, 0xff ) << endl;
|
|
}
|
|
|
|
unsigned long long millitime()
|
|
{
|
|
unsigned long long iTime = 0;
|
|
#ifdef _WIN32
|
|
struct timeb tm;
|
|
ftime( &tm );
|
|
iTime = tm.time * 1000;
|
|
iTime += tm.millitm;
|
|
#else
|
|
struct timeval tv;
|
|
gettimeofday( &tv, NULL );
|
|
iTime = ( unsigned long long )tv.tv_sec * 1000;
|
|
iTime += (( unsigned long long )tv.tv_usec / 1000 );
|
|
#endif /* _WIN32 */
|
|
return( iTime );
|
|
}
|
|
|
|
#ifndef _NO_CSOCKET_NS // some people may not want to use a namespace
|
|
}
|
|
using namespace Csocket;
|
|
#endif /* _NO_CSOCKET_NS */
|
|
|
|
CCron::CCron()
|
|
{
|
|
m_iCycles = 0;
|
|
m_iMaxCycles = 0;
|
|
m_bActive = true;
|
|
timerclear( &m_tTime );
|
|
m_tTimeSequence.tv_sec = 60;
|
|
m_tTimeSequence.tv_usec = 0;
|
|
m_bPause = false;
|
|
m_bRunOnNextCall = false;
|
|
}
|
|
|
|
void CCron::run( timeval & tNow )
|
|
{
|
|
if( m_bPause )
|
|
return;
|
|
|
|
if( !timerisset( &tNow ) )
|
|
gettimeofday( &tNow, NULL );
|
|
|
|
if(( m_bActive ) && ( !timercmp( &tNow, &m_tTime, < ) || m_bRunOnNextCall ) )
|
|
{
|
|
m_bRunOnNextCall = false; // Setting this here because RunJob() could set it back to true
|
|
RunJob();
|
|
|
|
if(( m_iMaxCycles > 0 ) && ( ++m_iCycles >= m_iMaxCycles ) )
|
|
m_bActive = false;
|
|
else
|
|
timeradd( &tNow, &m_tTimeSequence, &m_tTime );
|
|
}
|
|
}
|
|
|
|
void CCron::StartMaxCycles( double fTimeSequence, u_int iMaxCycles )
|
|
{
|
|
timeval tNow;
|
|
m_tTimeSequence.tv_sec = (time_t) fTimeSequence;
|
|
m_tTimeSequence.tv_usec = (suseconds_t) ((fTimeSequence - (time_t) fTimeSequence) * 1000000);
|
|
gettimeofday( &tNow, NULL );
|
|
timeradd( &tNow, &m_tTimeSequence, &m_tTime );
|
|
m_iMaxCycles = iMaxCycles;
|
|
}
|
|
|
|
void CCron::StartMaxCycles( const timeval& tTimeSequence, u_int iMaxCycles )
|
|
{
|
|
timeval tNow;
|
|
m_tTimeSequence = tTimeSequence;
|
|
gettimeofday( &tNow, NULL );
|
|
timeradd( &tNow, &m_tTimeSequence, &m_tTime );
|
|
m_iMaxCycles = iMaxCycles;
|
|
}
|
|
|
|
void CCron::Start( double fTimeSequence )
|
|
{
|
|
StartMaxCycles( fTimeSequence, 0 );
|
|
}
|
|
|
|
void CCron::Start( const timeval& tTimeSequence )
|
|
{
|
|
StartMaxCycles( tTimeSequence, 0 );
|
|
}
|
|
|
|
void CCron::Stop()
|
|
{
|
|
m_bActive = false;
|
|
}
|
|
|
|
void CCron::Pause()
|
|
{
|
|
m_bPause = true;
|
|
}
|
|
|
|
void CCron::UnPause()
|
|
{
|
|
m_bPause = false;
|
|
}
|
|
|
|
timeval CCron::GetInterval() const { return( m_tTimeSequence ); }
|
|
u_int CCron::GetMaxCycles() const { return( m_iMaxCycles ); }
|
|
u_int CCron::GetCyclesLeft() const { return(( m_iMaxCycles > m_iCycles ? ( m_iMaxCycles - m_iCycles ) : 0 ) ); }
|
|
|
|
bool CCron::isValid() { return( m_bActive ); }
|
|
const CS_STRING & CCron::GetName() const { return( m_sName ); }
|
|
void CCron::SetName( const CS_STRING & sName ) { m_sName = sName; }
|
|
void CCron::RunJob() { CS_DEBUG( "This should be overridden" ); }
|
|
|
|
bool CSMonitorFD::GatherFDsForSelect( std::map< int, short > & miiReadyFds, long & iTimeoutMS )
|
|
{
|
|
iTimeoutMS = -1; // don't bother changing anything in the default implementation
|
|
for( std::map< int, short >::iterator it = m_miiMonitorFDs.begin(); it != m_miiMonitorFDs.end(); ++it )
|
|
{
|
|
miiReadyFds[it->first] = it->second;
|
|
}
|
|
return( m_bEnabled );
|
|
}
|
|
|
|
bool CSMonitorFD::CheckFDs( const std::map< int, short > & miiReadyFds )
|
|
{
|
|
std::map< int, short > miiTriggerdFds;
|
|
for( std::map< int, short >::iterator it = m_miiMonitorFDs.begin(); it != m_miiMonitorFDs.end(); ++it )
|
|
{
|
|
std::map< int, short >::const_iterator itFD = miiReadyFds.find( it->first );
|
|
if( itFD != miiReadyFds.end() )
|
|
miiTriggerdFds[itFD->first] = itFD->second;
|
|
}
|
|
if( miiTriggerdFds.size() )
|
|
return( FDsThatTriggered( miiTriggerdFds ) );
|
|
return( m_bEnabled );
|
|
}
|
|
|
|
CSockCommon::~CSockCommon()
|
|
{
|
|
// delete any left over crons
|
|
CleanupCrons();
|
|
CleanupFDMonitors();
|
|
}
|
|
|
|
void CSockCommon::CleanupCrons()
|
|
{
|
|
for( size_t a = 0; a < m_vcCrons.size(); a++ )
|
|
CS_Delete( m_vcCrons[a] );
|
|
m_vcCrons.clear();
|
|
}
|
|
|
|
void CSockCommon::CleanupFDMonitors()
|
|
{
|
|
for( size_t a = 0; a < m_vcMonitorFD.size(); a++ )
|
|
CS_Delete( m_vcMonitorFD[a] );
|
|
m_vcMonitorFD.clear();
|
|
}
|
|
|
|
void CSockCommon::CheckFDs( const std::map< int, short > & miiReadyFds )
|
|
{
|
|
for( size_t uMon = 0; uMon < m_vcMonitorFD.size(); ++uMon )
|
|
{
|
|
if( !m_vcMonitorFD[uMon]->IsEnabled() || !m_vcMonitorFD[uMon]->CheckFDs( miiReadyFds ) )
|
|
m_vcMonitorFD.erase( m_vcMonitorFD.begin() + uMon-- );
|
|
}
|
|
}
|
|
|
|
void CSockCommon::AssignFDs( std::map< int, short > & miiReadyFds, struct timeval * tvtimeout )
|
|
{
|
|
for( size_t uMon = 0; uMon < m_vcMonitorFD.size(); ++uMon )
|
|
{
|
|
long iTimeoutMS = -1;
|
|
if( m_vcMonitorFD[uMon]->IsEnabled() && m_vcMonitorFD[uMon]->GatherFDsForSelect( miiReadyFds, iTimeoutMS ) )
|
|
{
|
|
CSAdjustTVTimeout( *tvtimeout, iTimeoutMS );
|
|
}
|
|
else
|
|
{
|
|
CS_Delete( m_vcMonitorFD[uMon] );
|
|
m_vcMonitorFD.erase( m_vcMonitorFD.begin() + uMon-- );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CSockCommon::Cron()
|
|
{
|
|
timeval tNow;
|
|
timerclear( &tNow );
|
|
|
|
for( vector<CCron *>::size_type a = 0; a < m_vcCrons.size(); a++ )
|
|
{
|
|
CCron *pcCron = m_vcCrons[a];
|
|
|
|
if( !pcCron->isValid() )
|
|
{
|
|
CS_Delete( pcCron );
|
|
m_vcCrons.erase( m_vcCrons.begin() + a-- );
|
|
}
|
|
else
|
|
pcCron->run( tNow );
|
|
}
|
|
}
|
|
|
|
void CSockCommon::AddCron( CCron * pcCron )
|
|
{
|
|
m_vcCrons.push_back( pcCron );
|
|
}
|
|
|
|
void CSockCommon::DelCron( const CS_STRING & sName, bool bDeleteAll, bool bCaseSensitive )
|
|
{
|
|
for( u_int a = 0; a < m_vcCrons.size(); a++ )
|
|
{
|
|
int ( *Cmp )( const char *, const char * ) = ( bCaseSensitive ? strcmp : strcasecmp );
|
|
if( Cmp( m_vcCrons[a]->GetName().c_str(), sName.c_str() ) == 0 )
|
|
{
|
|
m_vcCrons[a]->Stop();
|
|
CS_Delete( m_vcCrons[a] );
|
|
m_vcCrons.erase( m_vcCrons.begin() + a-- );
|
|
if( !bDeleteAll )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSockCommon::DelCron( u_int iPos )
|
|
{
|
|
if( iPos < m_vcCrons.size() )
|
|
{
|
|
m_vcCrons[iPos]->Stop();
|
|
CS_Delete( m_vcCrons[iPos] );
|
|
m_vcCrons.erase( m_vcCrons.begin() + iPos );
|
|
}
|
|
}
|
|
|
|
void CSockCommon::DelCronByAddr( CCron *pcCron )
|
|
{
|
|
for( u_int a = 0; a < m_vcCrons.size(); a++ )
|
|
{
|
|
if( m_vcCrons[a] == pcCron )
|
|
{
|
|
m_vcCrons[a]->Stop();
|
|
CS_Delete( m_vcCrons[a] );
|
|
m_vcCrons.erase( m_vcCrons.begin() + a );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Csock::Csock( int iTimeout ) : CSockCommon()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
m_pCerVerifyCB = NULL;
|
|
#endif /* HAVE_LIBSSL */
|
|
Init( "", 0, iTimeout );
|
|
}
|
|
|
|
Csock::Csock( const CS_STRING & sHostname, u_short iport, int iTimeout ) : CSockCommon()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
m_pCerVerifyCB = NULL;
|
|
#endif /* HAVE_LIBSSL */
|
|
Init( sHostname, iport, iTimeout );
|
|
}
|
|
|
|
// override this for accept sockets
|
|
Csock *Csock::GetSockObj( const CS_STRING & sHostname, u_short iPort )
|
|
{
|
|
return( NULL );
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#define CS_CLOSE closesocket
|
|
#else
|
|
#define CS_CLOSE close
|
|
#endif /* _WIN32 */
|
|
|
|
Csock::~Csock()
|
|
{
|
|
#ifdef _WIN32
|
|
// prevent successful closesocket() calls and such from
|
|
// overwriting any possible previous errors.
|
|
int iOldError = ::WSAGetLastError();
|
|
#endif /* _WIN32 */
|
|
|
|
#ifdef HAVE_C_ARES
|
|
if( m_pARESChannel )
|
|
ares_cancel( m_pARESChannel );
|
|
FreeAres();
|
|
#endif /* HAVE_C_ARES */
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
FREE_SSL();
|
|
FREE_CTX();
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
CloseSocksFD();
|
|
|
|
#ifdef _WIN32
|
|
::WSASetLastError( iOldError );
|
|
#endif /* _WIN32 */
|
|
}
|
|
|
|
void Csock::CloseSocksFD()
|
|
{
|
|
if( m_iReadSock != m_iWriteSock )
|
|
{
|
|
if( m_iReadSock != CS_INVALID_SOCK )
|
|
CS_CLOSE( m_iReadSock );
|
|
if( m_iWriteSock != CS_INVALID_SOCK )
|
|
CS_CLOSE( m_iWriteSock );
|
|
}
|
|
else if( m_iReadSock != CS_INVALID_SOCK )
|
|
CS_CLOSE( m_iReadSock );
|
|
|
|
m_iReadSock = CS_INVALID_SOCK;
|
|
m_iWriteSock = CS_INVALID_SOCK;
|
|
}
|
|
|
|
|
|
void Csock::Dereference()
|
|
{
|
|
m_iWriteSock = m_iReadSock = CS_INVALID_SOCK;
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
m_ssl = NULL;
|
|
m_ssl_ctx = NULL;
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
// don't delete and erase, just erase since they were moved to the copied sock
|
|
m_vcCrons.clear();
|
|
m_vcMonitorFD.clear();
|
|
Close( CLT_DEREFERENCE );
|
|
}
|
|
|
|
void Csock::Copy( const Csock & cCopy )
|
|
{
|
|
m_iTcount = cCopy.m_iTcount;
|
|
m_iLastCheckTimeoutTime = cCopy.m_iLastCheckTimeoutTime;
|
|
m_uPort = cCopy.m_uPort;
|
|
m_iRemotePort = cCopy.m_iRemotePort;
|
|
m_iLocalPort = cCopy.m_iLocalPort;
|
|
m_iReadSock = cCopy.m_iReadSock;
|
|
m_iWriteSock = cCopy.m_iWriteSock;
|
|
m_iTimeout = cCopy.m_iTimeout;
|
|
m_iMaxConns = cCopy.m_iMaxConns;
|
|
m_iConnType = cCopy.m_iConnType;
|
|
m_iMethod = cCopy.m_iMethod;
|
|
m_bUseSSL = cCopy.m_bUseSSL;
|
|
m_bIsConnected = cCopy.m_bIsConnected;
|
|
m_bsslEstablished = cCopy.m_bsslEstablished;
|
|
m_bEnableReadLine = cCopy.m_bEnableReadLine;
|
|
m_bPauseRead = cCopy.m_bPauseRead;
|
|
m_shostname = cCopy.m_shostname;
|
|
m_sbuffer = cCopy.m_sbuffer;
|
|
m_sSockName = cCopy.m_sSockName;
|
|
m_sPemFile = cCopy.m_sPemFile;
|
|
m_sCipherType = cCopy.m_sCipherType;
|
|
m_sParentName = cCopy.m_sParentName;
|
|
m_sSend = cCopy.m_sSend;
|
|
m_sPemPass = cCopy.m_sPemPass;
|
|
m_sLocalIP = cCopy.m_sLocalIP;
|
|
m_sRemoteIP = cCopy.m_sRemoteIP;
|
|
m_eCloseType = cCopy.m_eCloseType;
|
|
|
|
m_iMaxMilliSeconds = cCopy.m_iMaxMilliSeconds;
|
|
m_iLastSendTime = cCopy.m_iLastSendTime;
|
|
m_iBytesRead = cCopy.m_iBytesRead;
|
|
m_iBytesWritten = cCopy.m_iBytesWritten;
|
|
m_iStartTime = cCopy.m_iStartTime;
|
|
m_iMaxBytes = cCopy.m_iMaxBytes;
|
|
m_iLastSend = cCopy.m_iLastSend;
|
|
m_iMaxStoredBufferLength = cCopy.m_iMaxStoredBufferLength;
|
|
m_iTimeoutType = cCopy.m_iTimeoutType;
|
|
|
|
m_address = cCopy.m_address;
|
|
m_bindhost = cCopy.m_bindhost;
|
|
m_bIsIPv6 = cCopy.m_bIsIPv6;
|
|
m_bSkipConnect = cCopy.m_bSkipConnect;
|
|
#ifdef HAVE_C_ARES
|
|
FreeAres(); // Not copying this state, but making sure its nulled out
|
|
m_iARESStatus = -1; // set it to unitialized
|
|
m_pCurrAddr = NULL;
|
|
#endif /* HAVE_C_ARES */
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
m_iRequireClientCertFlags = cCopy.m_iRequireClientCertFlags;
|
|
m_sSSLBuffer = cCopy.m_sSSLBuffer;
|
|
|
|
FREE_SSL();
|
|
FREE_CTX(); // be sure to remove anything that was already here
|
|
m_ssl = cCopy.m_ssl;
|
|
m_ssl_ctx = cCopy.m_ssl_ctx;
|
|
|
|
m_pCerVerifyCB = cCopy.m_pCerVerifyCB;
|
|
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
CleanupCrons();
|
|
CleanupFDMonitors();
|
|
m_vcCrons = cCopy.m_vcCrons;
|
|
m_vcMonitorFD = cCopy.m_vcMonitorFD;
|
|
|
|
m_eConState = cCopy.m_eConState;
|
|
m_sBindHost = cCopy.m_sBindHost;
|
|
m_iCurBindCount = cCopy.m_iCurBindCount;
|
|
m_iDNSTryCount = cCopy.m_iDNSTryCount;
|
|
|
|
}
|
|
|
|
Csock & Csock::operator<<( const CS_STRING & s )
|
|
{
|
|
Write( s );
|
|
return( *this );
|
|
}
|
|
|
|
Csock & Csock::operator<<( ostream & ( *io )( ostream & ) )
|
|
{
|
|
Write( "\r\n" );
|
|
return( *this );
|
|
}
|
|
|
|
Csock & Csock::operator<<( int i )
|
|
{
|
|
stringstream s;
|
|
s << i;
|
|
Write( s.str() );
|
|
return( *this );
|
|
}
|
|
Csock & Csock::operator<<( unsigned int i )
|
|
{
|
|
stringstream s;
|
|
s << i;
|
|
Write( s.str() );
|
|
return( *this );
|
|
}
|
|
Csock & Csock::operator<<( long i )
|
|
{
|
|
stringstream s;
|
|
s << i;
|
|
Write( s.str() );
|
|
return( *this );
|
|
}
|
|
Csock & Csock::operator<<( unsigned long i )
|
|
{
|
|
stringstream s;
|
|
s << i;
|
|
Write( s.str() );
|
|
return( *this );
|
|
}
|
|
Csock & Csock::operator<<( unsigned long long i )
|
|
{
|
|
stringstream s;
|
|
s << i;
|
|
Write( s.str() );
|
|
return( *this );
|
|
}
|
|
Csock & Csock::operator<<( float i )
|
|
{
|
|
stringstream s;
|
|
s << i;
|
|
Write( s.str() );
|
|
return( *this );
|
|
}
|
|
Csock & Csock::operator<<( double i )
|
|
{
|
|
stringstream s;
|
|
s << i;
|
|
Write( s.str() );
|
|
return( *this );
|
|
}
|
|
|
|
bool Csock::Connect()
|
|
{
|
|
if( m_bSkipConnect )
|
|
{
|
|
// this was already called, so skipping now. this is to allow easy pass through
|
|
if( m_eConState != CST_OK )
|
|
{
|
|
m_eConState = ( GetSSL() ? CST_CONNECTSSL : CST_OK );
|
|
}
|
|
return( true );
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
set_non_blocking( m_iReadSock );
|
|
#else
|
|
if( !GetIPv6() )
|
|
set_non_blocking( m_iReadSock );
|
|
// non-blocking sockets on Win32 do *not* return ENETUNREACH/EHOSTUNREACH if there's no IPv6 gateway.
|
|
// we need those error codes for the v4 fallback in GetAddrInfo!
|
|
#endif /* _WIN32 */
|
|
|
|
m_iConnType = OUTBOUND;
|
|
|
|
int ret = -1;
|
|
if( !GetIPv6() )
|
|
ret = connect( m_iReadSock, ( struct sockaddr * )m_address.GetSockAddr(), m_address.GetSockAddrLen() );
|
|
#ifdef HAVE_IPV6
|
|
else
|
|
ret = connect( m_iReadSock, ( struct sockaddr * )m_address.GetSockAddr6(), m_address.GetSockAddrLen6() );
|
|
#endif /* HAVE_IPV6 */
|
|
#ifndef _WIN32
|
|
if( ret == -1 && GetSockError() != EINPROGRESS )
|
|
#else
|
|
if( ret == -1 && GetSockError() != EINPROGRESS && GetSockError() != WSAEWOULDBLOCK )
|
|
#endif /* _WIN32 */
|
|
|
|
{
|
|
CS_DEBUG( "Connect Failed. ERRNO [" << GetSockError() << "] FD [" << m_iReadSock << "]" );
|
|
return( false );
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// do what we didn't do above since connect() is now over!
|
|
if( GetIPv6() )
|
|
set_non_blocking( m_iReadSock );
|
|
#endif /* _WIN32 */
|
|
|
|
if( m_eConState != CST_OK )
|
|
{
|
|
m_eConState = ( GetSSL() ? CST_CONNECTSSL : CST_OK );
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
|
|
|
|
bool Csock::Listen( u_short iPort, int iMaxConns, const CS_STRING & sBindHost, u_int iTimeout, bool bDetach )
|
|
{
|
|
m_iConnType = LISTENER;
|
|
m_iTimeout = iTimeout;
|
|
m_sBindHost = sBindHost;
|
|
m_iMaxConns = iMaxConns;
|
|
|
|
SetConState( Csock::CST_OK );
|
|
if( !m_sBindHost.empty() )
|
|
{
|
|
if( bDetach )
|
|
{
|
|
int iRet = GetAddrInfo( m_sBindHost, m_address );
|
|
if( iRet == ETIMEDOUT )
|
|
{
|
|
CallSockError( EADDRNOTAVAIL );
|
|
return( false );
|
|
}
|
|
else if( iRet == EAGAIN )
|
|
{
|
|
SetConState( Csock::CST_BINDVHOST );
|
|
return( true );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if not detaching, then must block to do DNS resolution, so might as well use internal resolver
|
|
if( ::GetAddrInfo( m_sBindHost, this, m_address ) != 0 )
|
|
{
|
|
CallSockError( EADDRNOTAVAIL );
|
|
return( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
m_iReadSock = m_iWriteSock = CreateSocket( true );
|
|
|
|
if( m_iReadSock == CS_INVALID_SOCK )
|
|
{
|
|
CallSockError( EBADF );
|
|
return( false );
|
|
}
|
|
|
|
#ifdef HAVE_IPV6
|
|
#ifdef _WIN32
|
|
# ifndef IPPROTO_IPV6
|
|
# define IPPROTO_IPV6 41 /* define for apps with _WIN32_WINNT < 0x0501 (XP) */
|
|
# endif /* !IPPROTO_IPV6 */
|
|
/* check for IPV6_V6ONLY support at runtime */
|
|
OSVERSIONINFOW lvi = { sizeof( OSVERSIONINFOW ), 0 };
|
|
if( ::GetVersionExW( &lvi ) && lvi.dwMajorVersion >= 6 ) // IPV6_V6ONLY is supported on Windows Vista or later.
|
|
{
|
|
#endif /* _WIN32 */
|
|
if( GetIPv6() )
|
|
{
|
|
// per RFC3493#5.3
|
|
const int on = ( m_address.GetAFRequire() == CSSockAddr::RAF_INET6 ? 1 : 0 );
|
|
if( setsockopt( m_iReadSock, IPPROTO_IPV6, IPV6_V6ONLY, ( char * )&on, sizeof( on ) ) != 0 )
|
|
PERROR( "IPV6_V6ONLY" );
|
|
}
|
|
#ifdef _WIN32
|
|
}
|
|
#endif /* _WIN32 */
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
m_address.SinFamily();
|
|
m_address.SinPort( iPort );
|
|
if( !GetIPv6() )
|
|
{
|
|
if( bind( m_iReadSock, ( struct sockaddr * ) m_address.GetSockAddr(), m_address.GetSockAddrLen() ) == -1 )
|
|
{
|
|
CallSockError( GetSockError() );
|
|
return( false );
|
|
}
|
|
}
|
|
#ifdef HAVE_IPV6
|
|
else
|
|
{
|
|
if( bind( m_iReadSock, ( struct sockaddr * ) m_address.GetSockAddr6(), m_address.GetSockAddrLen6() ) == -1 )
|
|
{
|
|
CallSockError( GetSockError() );
|
|
return( false );
|
|
}
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
if( listen( m_iReadSock, iMaxConns ) == -1 )
|
|
{
|
|
CallSockError( GetSockError() );
|
|
return( false );
|
|
}
|
|
|
|
// set it none blocking
|
|
set_non_blocking( m_iReadSock );
|
|
if( m_uPort == 0 || m_sBindHost.size() )
|
|
{
|
|
struct sockaddr_storage cAddr;
|
|
socklen_t iAddrLen = sizeof( cAddr );
|
|
if( getsockname( m_iReadSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 )
|
|
{
|
|
ConvertAddress( &cAddr, iAddrLen, m_sBindHost, &m_uPort );
|
|
}
|
|
}
|
|
Listening( m_sBindHost, m_uPort );
|
|
|
|
return( true );
|
|
}
|
|
|
|
cs_sock_t Csock::Accept( CS_STRING & sHost, u_short & iRPort )
|
|
{
|
|
cs_sock_t iSock = CS_INVALID_SOCK;
|
|
struct sockaddr_storage cAddr;
|
|
socklen_t iAddrLen = sizeof( cAddr );
|
|
iSock = accept( m_iReadSock, ( struct sockaddr * )&cAddr, &iAddrLen );
|
|
if( iSock != CS_INVALID_SOCK && getpeername( iSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 )
|
|
{
|
|
ConvertAddress( &cAddr, iAddrLen, sHost, &iRPort );
|
|
}
|
|
|
|
if( iSock != CS_INVALID_SOCK )
|
|
{
|
|
// Make it close-on-exec
|
|
set_close_on_exec( iSock );
|
|
|
|
// make it none blocking
|
|
set_non_blocking( iSock );
|
|
|
|
if( !ConnectionFrom( sHost, iRPort ) )
|
|
{
|
|
CS_CLOSE( iSock );
|
|
iSock = CS_INVALID_SOCK;
|
|
}
|
|
|
|
}
|
|
|
|
return( iSock );
|
|
}
|
|
|
|
bool Csock::AcceptSSL()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
if( !m_ssl )
|
|
if( !SSLServerSetup() )
|
|
return( false );
|
|
|
|
int err = SSL_accept( m_ssl );
|
|
|
|
if( err == 1 )
|
|
{
|
|
return( true );
|
|
}
|
|
|
|
int sslErr = SSL_get_error( m_ssl, err );
|
|
|
|
if( sslErr == SSL_ERROR_WANT_READ || sslErr == SSL_ERROR_WANT_WRITE )
|
|
return( true );
|
|
|
|
SSLErrors( __FILE__, __LINE__ );
|
|
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
return( false );
|
|
}
|
|
|
|
bool Csock::SSLClientSetup()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
m_bUseSSL = true;
|
|
FREE_SSL();
|
|
FREE_CTX();
|
|
|
|
#ifdef _WIN64
|
|
if( m_iReadSock != ( int )m_iReadSock || m_iWriteSock != ( int )m_iWriteSock )
|
|
{
|
|
// sanity check the FD to be sure its compatible with openssl
|
|
CS_DEBUG( "ERROR: sockfd larger than OpenSSL can handle" );
|
|
return( false );
|
|
}
|
|
#endif /* _WIN64 */
|
|
|
|
switch( m_iMethod )
|
|
{
|
|
case SSL3:
|
|
m_ssl_ctx = SSL_CTX_new( SSLv3_client_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... SSLv3_client_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
case TLS1:
|
|
m_ssl_ctx = SSL_CTX_new( TLSv1_client_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... TLSv1_client_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
case SSL2:
|
|
#ifndef OPENSSL_NO_SSL2
|
|
m_ssl_ctx = SSL_CTX_new( SSLv2_client_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... SSLv2_client_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
#endif
|
|
/* Fall through if SSL2 is disabled */
|
|
case SSL23:
|
|
default:
|
|
m_ssl_ctx = SSL_CTX_new( SSLv23_client_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... SSLv23_client_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
SSL_CTX_set_default_verify_paths( m_ssl_ctx );
|
|
|
|
if( !m_sPemFile.empty() )
|
|
{
|
|
// are we sending a client cerificate ?
|
|
SSL_CTX_set_default_passwd_cb( m_ssl_ctx, PemPassCB );
|
|
SSL_CTX_set_default_passwd_cb_userdata( m_ssl_ctx, ( void * )this );
|
|
|
|
//
|
|
// set up the CTX
|
|
if( SSL_CTX_use_certificate_file( m_ssl_ctx, m_sPemFile.c_str() , SSL_FILETYPE_PEM ) <= 0 )
|
|
{
|
|
CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" );
|
|
SSLErrors( __FILE__, __LINE__ );
|
|
}
|
|
|
|
if( SSL_CTX_use_PrivateKey_file( m_ssl_ctx, m_sPemFile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
|
|
{
|
|
CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" );
|
|
SSLErrors( __FILE__, __LINE__ );
|
|
}
|
|
}
|
|
|
|
m_ssl = SSL_new( m_ssl_ctx );
|
|
if( !m_ssl )
|
|
return( false );
|
|
|
|
SSL_set_rfd( m_ssl, ( int )m_iReadSock );
|
|
SSL_set_wfd( m_ssl, ( int )m_iWriteSock );
|
|
SSL_set_verify( m_ssl, SSL_VERIFY_PEER, ( m_pCerVerifyCB ? m_pCerVerifyCB : CertVerifyCB ) );
|
|
SSL_set_ex_data( m_ssl, GetCsockClassIdx(), this );
|
|
|
|
SSLFinishSetup( m_ssl );
|
|
return( true );
|
|
#else
|
|
return( false );
|
|
|
|
#endif /* HAVE_LIBSSL */
|
|
}
|
|
|
|
bool Csock::SSLServerSetup()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
m_bUseSSL = true;
|
|
FREE_SSL();
|
|
FREE_CTX();
|
|
|
|
#ifdef _WIN64
|
|
if( m_iReadSock != ( int )m_iReadSock || m_iWriteSock != ( int )m_iWriteSock )
|
|
{
|
|
// sanity check the FD to be sure its compatible with openssl
|
|
CS_DEBUG( "ERROR: sockfd larger than OpenSSL can handle" );
|
|
return( false );
|
|
}
|
|
#endif /* _WIN64 */
|
|
|
|
|
|
switch( m_iMethod )
|
|
{
|
|
case SSL3:
|
|
m_ssl_ctx = SSL_CTX_new( SSLv3_server_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... SSLv3_server_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
|
|
case TLS1:
|
|
m_ssl_ctx = SSL_CTX_new( TLSv1_server_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... TLSv1_server_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
#ifndef OPENSSL_NO_SSL2
|
|
case SSL2:
|
|
m_ssl_ctx = SSL_CTX_new( SSLv2_server_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... SSLv2_server_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
#endif
|
|
/* Fall through if SSL2 is disabled */
|
|
case SSL23:
|
|
default:
|
|
m_ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
|
|
if( !m_ssl_ctx )
|
|
{
|
|
CS_DEBUG( "WARNING: MakeConnection .... SSLv23_server_method failed!" );
|
|
return( false );
|
|
}
|
|
break;
|
|
}
|
|
|
|
SSL_CTX_set_default_verify_paths( m_ssl_ctx );
|
|
|
|
// set the pemfile password
|
|
SSL_CTX_set_default_passwd_cb( m_ssl_ctx, PemPassCB );
|
|
SSL_CTX_set_default_passwd_cb_userdata( m_ssl_ctx, ( void * )this );
|
|
|
|
if( m_sPemFile.empty() || access( m_sPemFile.c_str(), R_OK ) != 0 )
|
|
{
|
|
CS_DEBUG( "There is a problem with [" << m_sPemFile << "]" );
|
|
return( false );
|
|
}
|
|
|
|
//
|
|
// set up the CTX
|
|
if( SSL_CTX_use_certificate_chain_file( m_ssl_ctx, m_sPemFile.c_str() ) <= 0 )
|
|
{
|
|
CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" );
|
|
SSLErrors( __FILE__, __LINE__ );
|
|
return( false );
|
|
}
|
|
|
|
if( SSL_CTX_use_PrivateKey_file( m_ssl_ctx, m_sPemFile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
|
|
{
|
|
CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" );
|
|
SSLErrors( __FILE__, __LINE__ );
|
|
return( false );
|
|
}
|
|
|
|
// check to see if this pem file contains a DH structure for use with DH key exchange
|
|
// https://github.com/znc/znc/pull/46
|
|
FILE *dhParamsFile = fopen( m_sPemFile.c_str(), "r" );
|
|
if( !dhParamsFile )
|
|
{
|
|
CS_DEBUG( "There is a problem with [" << m_sPemFile << "]" );
|
|
return( false );
|
|
}
|
|
|
|
DH * dhParams = PEM_read_DHparams( dhParamsFile, NULL, NULL, NULL );
|
|
fclose( dhParamsFile );
|
|
if( dhParams )
|
|
{
|
|
SSL_CTX_set_options( m_ssl_ctx, SSL_OP_SINGLE_DH_USE );
|
|
if( !SSL_CTX_set_tmp_dh( m_ssl_ctx, dhParams ) )
|
|
{
|
|
CS_DEBUG( "Error setting ephemeral DH parameters from [" << m_sPemFile << "]" );
|
|
SSLErrors( __FILE__, __LINE__ );
|
|
DH_free( dhParams );
|
|
return( false );
|
|
}
|
|
DH_free( dhParams );
|
|
}
|
|
else
|
|
{
|
|
// Presumably PEM_read_DHparams failed, as there was no DH structure. Clearing those errors here so they are removed off the stack
|
|
ERR_clear_error();
|
|
}
|
|
|
|
if( SSL_CTX_set_cipher_list( m_ssl_ctx, m_sCipherType.c_str() ) <= 0 )
|
|
{
|
|
CS_DEBUG( "Could not assign cipher [" << m_sCipherType << "]" );
|
|
return( false );
|
|
}
|
|
|
|
//
|
|
// setup the SSL
|
|
m_ssl = SSL_new( m_ssl_ctx );
|
|
if( !m_ssl )
|
|
return( false );
|
|
|
|
// Call for client Verification
|
|
SSL_set_rfd( m_ssl, ( int )m_iReadSock );
|
|
SSL_set_wfd( m_ssl, ( int )m_iWriteSock );
|
|
SSL_set_accept_state( m_ssl );
|
|
if( m_iRequireClientCertFlags )
|
|
{
|
|
SSL_set_verify( m_ssl, m_iRequireClientCertFlags, ( m_pCerVerifyCB ? m_pCerVerifyCB : CertVerifyCB ) );
|
|
SSL_set_ex_data( m_ssl, GetCsockClassIdx(), this );
|
|
}
|
|
|
|
SSLFinishSetup( m_ssl );
|
|
return( true );
|
|
#else
|
|
return( false );
|
|
#endif /* HAVE_LIBSSL */
|
|
}
|
|
|
|
bool Csock::StartTLS()
|
|
{
|
|
if( m_iConnType == INBOUND )
|
|
return( AcceptSSL() );
|
|
if( m_iConnType == OUTBOUND )
|
|
return( ConnectSSL() );
|
|
CS_DEBUG( "Invalid connection type with StartTLS" );
|
|
return( false );
|
|
}
|
|
|
|
bool Csock::ConnectSSL()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
if( m_iReadSock == CS_INVALID_SOCK )
|
|
return( false ); // this should be long passed at this point
|
|
if( !m_ssl && !SSLClientSetup() )
|
|
return( false );
|
|
|
|
bool bPass = true;
|
|
|
|
int iErr = SSL_connect( m_ssl );
|
|
if( iErr != 1 )
|
|
{
|
|
int sslErr = SSL_get_error( m_ssl, iErr );
|
|
bPass = false;
|
|
if( sslErr == SSL_ERROR_WANT_READ || sslErr == SSL_ERROR_WANT_WRITE )
|
|
bPass = true;
|
|
#ifdef _WIN32
|
|
else if( sslErr == SSL_ERROR_SYSCALL && iErr < 0 && GetLastError() == WSAENOTCONN )
|
|
{
|
|
// this seems to be an issue with win32 only. I've seen it happen on slow connections
|
|
// the issue is calling this before select(), which isn't a problem on unix. Allowing this
|
|
// to pass in this case is fine because subsequent ssl transactions will occur and the handshake
|
|
// will finish. At this point, its just instantiating the handshake.
|
|
bPass = true;
|
|
}
|
|
#endif /* _WIN32 */
|
|
}
|
|
else
|
|
bPass = true;
|
|
|
|
if( m_eConState != CST_OK )
|
|
m_eConState = CST_OK;
|
|
return( bPass );
|
|
#else
|
|
return( false );
|
|
#endif /* HAVE_LIBSSL */
|
|
}
|
|
|
|
bool Csock::AllowWrite( unsigned long long & iNOW ) const
|
|
{
|
|
if( m_iMaxBytes > 0 && m_iMaxMilliSeconds > 0 )
|
|
{
|
|
if( iNOW == 0 )
|
|
iNOW = millitime();
|
|
|
|
if( m_iLastSend < m_iMaxBytes )
|
|
return( true ); // allow sending if our out buffer was less than what we can send
|
|
if(( iNOW - m_iLastSendTime ) < m_iMaxMilliSeconds )
|
|
return( false );
|
|
}
|
|
return( true );
|
|
}
|
|
|
|
bool Csock::Write( const char *data, size_t len )
|
|
{
|
|
m_sSend.append( data, len );
|
|
|
|
if( m_sSend.empty() )
|
|
return( true );
|
|
|
|
if( m_eConState != CST_OK )
|
|
return( true );
|
|
|
|
// rate shaping
|
|
u_long iBytesToSend = 0;
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
if( m_bUseSSL && m_sSSLBuffer.empty() && !m_bsslEstablished )
|
|
{
|
|
// to keep openssl from spinning, just initiate the connection with 1 byte so the connection establishes faster
|
|
iBytesToSend = 1;
|
|
}
|
|
else
|
|
#endif /* HAVE_LIBSSL */
|
|
if( m_iMaxBytes > 0 && m_iMaxMilliSeconds > 0 )
|
|
{
|
|
unsigned long long iNOW = millitime();
|
|
// figure out the shaping here
|
|
// if NOW - m_iLastSendTime > m_iMaxMilliSeconds then send a full length of ( iBytesToSend )
|
|
if(( iNOW - m_iLastSendTime ) > m_iMaxMilliSeconds )
|
|
{
|
|
m_iLastSendTime = iNOW;
|
|
iBytesToSend = m_iMaxBytes;
|
|
m_iLastSend = 0;
|
|
|
|
}
|
|
else // otherwise send m_iMaxBytes - m_iLastSend
|
|
iBytesToSend = m_iMaxBytes - m_iLastSend;
|
|
|
|
// take which ever is lesser
|
|
if( m_sSend.length() < iBytesToSend )
|
|
iBytesToSend = m_sSend.length();
|
|
|
|
// add up the bytes sent
|
|
m_iLastSend += iBytesToSend;
|
|
|
|
// so, are we ready to send anything ?
|
|
if( iBytesToSend == 0 )
|
|
return( true );
|
|
|
|
}
|
|
else
|
|
iBytesToSend = m_sSend.length();
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
if( m_bUseSSL )
|
|
{
|
|
if( !m_ssl )
|
|
{
|
|
CS_DEBUG( "SSL object is NULL but m_bUseSSL is true" );
|
|
return( false );
|
|
}
|
|
|
|
if( m_sSSLBuffer.empty() ) // on retrying to write data, ssl wants the data in the SAME spot and the SAME size
|
|
m_sSSLBuffer.append( m_sSend.data(), iBytesToSend );
|
|
|
|
int iErr = SSL_write( m_ssl, m_sSSLBuffer.data(), ( int )m_sSSLBuffer.length() );
|
|
|
|
if( iErr < 0 && GetSockError() == ECONNREFUSED )
|
|
{
|
|
// If ret == -1, the underlying BIO reported an I/O error (man SSL_get_error)
|
|
ConnectionRefused();
|
|
return( false );
|
|
}
|
|
|
|
switch( SSL_get_error( m_ssl, iErr ) )
|
|
{
|
|
case SSL_ERROR_NONE:
|
|
m_bsslEstablished = true;
|
|
// all ok
|
|
break;
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
{
|
|
// weird closer alert
|
|
return( false );
|
|
}
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
// retry
|
|
break;
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
// retry
|
|
break;
|
|
|
|
case SSL_ERROR_SSL:
|
|
{
|
|
SSLErrors( __FILE__, __LINE__ );
|
|
return( false );
|
|
}
|
|
}
|
|
|
|
if( iErr > 0 )
|
|
{
|
|
m_sSSLBuffer.clear();
|
|
m_sSend.erase( 0, iErr );
|
|
// reset the timer on successful write (we have to set it here because the write
|
|
// bit might not always be set, so need to trigger)
|
|
if( TMO_WRITE & GetTimeoutType() )
|
|
ResetTimer();
|
|
|
|
m_iBytesWritten += ( unsigned long long )iErr;
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
#endif /* HAVE_LIBSSL */
|
|
#ifdef _WIN32
|
|
cs_ssize_t bytes = send( m_iWriteSock, m_sSend.data(), iBytesToSend, 0 );
|
|
#else
|
|
cs_ssize_t bytes = write( m_iWriteSock, m_sSend.data(), iBytesToSend );
|
|
#endif /* _WIN32 */
|
|
|
|
if( bytes == -1 && GetSockError() == ECONNREFUSED )
|
|
{
|
|
ConnectionRefused();
|
|
return( false );
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
if( bytes <= 0 && GetSockError() != WSAEWOULDBLOCK )
|
|
return( false );
|
|
#else
|
|
if( bytes <= 0 && GetSockError() != EAGAIN )
|
|
return( false );
|
|
#endif /* _WIN32 */
|
|
|
|
// delete the bytes we sent
|
|
if( bytes > 0 )
|
|
{
|
|
m_sSend.erase( 0, bytes );
|
|
if( TMO_WRITE & GetTimeoutType() )
|
|
ResetTimer(); // reset the timer on successful write
|
|
m_iBytesWritten += ( unsigned long long )bytes;
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
|
|
bool Csock::Write( const CS_STRING & sData )
|
|
{
|
|
return( Write( sData.c_str(), sData.length() ) );
|
|
}
|
|
|
|
cs_ssize_t Csock::Read( char *data, size_t len )
|
|
{
|
|
cs_ssize_t bytes = 0;
|
|
|
|
if( IsReadPaused() && SslIsEstablished() )
|
|
return( READ_EAGAIN ); // allow the handshake to complete first
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
if( m_bUseSSL )
|
|
{
|
|
if( !m_ssl )
|
|
{
|
|
CS_DEBUG( "SSL object is NULL but m_bUseSSL is true" );
|
|
return( READ_ERR );
|
|
}
|
|
|
|
bytes = SSL_read( m_ssl, data, ( int )len );
|
|
if( bytes >= 0 )
|
|
m_bsslEstablished = true; // this means all is good in the realm of ssl
|
|
}
|
|
else
|
|
#endif /* HAVE_LIBSSL */
|
|
#ifdef _WIN32
|
|
bytes = recv( m_iReadSock, data, len, 0 );
|
|
#else
|
|
bytes = read( m_iReadSock, data, len );
|
|
#endif /* _WIN32 */
|
|
if( bytes == -1 )
|
|
{
|
|
if( GetSockError() == ECONNREFUSED )
|
|
return( READ_CONNREFUSED );
|
|
|
|
if( GetSockError() == ETIMEDOUT )
|
|
return( READ_TIMEDOUT );
|
|
|
|
if( GetSockError() == EINTR || GetSockError() == EAGAIN )
|
|
return( READ_EAGAIN );
|
|
|
|
#ifdef _WIN32
|
|
if( GetSockError() == WSAEWOULDBLOCK )
|
|
return( READ_EAGAIN );
|
|
#endif /* _WIN32 */
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
if( m_ssl )
|
|
{
|
|
int iErr = SSL_get_error( m_ssl, ( int )bytes );
|
|
if( iErr != SSL_ERROR_WANT_READ && iErr != SSL_ERROR_WANT_WRITE )
|
|
return( READ_ERR );
|
|
else
|
|
return( READ_EAGAIN );
|
|
}
|
|
#else
|
|
return( READ_ERR );
|
|
#endif /* HAVE_LIBSSL */
|
|
}
|
|
|
|
if( bytes > 0 ) // becareful not to add negative bytes :P
|
|
m_iBytesRead += ( unsigned long long )bytes;
|
|
|
|
return( bytes );
|
|
}
|
|
|
|
CS_STRING Csock::GetLocalIP()
|
|
{
|
|
if( !m_sLocalIP.empty() )
|
|
return( m_sLocalIP );
|
|
|
|
cs_sock_t iSock = GetSock();
|
|
|
|
if( iSock == CS_INVALID_SOCK )
|
|
return( "" );
|
|
|
|
struct sockaddr_storage cAddr;
|
|
socklen_t iAddrLen = sizeof( cAddr );
|
|
if( getsockname( iSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 )
|
|
{
|
|
ConvertAddress( &cAddr, iAddrLen, m_sLocalIP, &m_iLocalPort );
|
|
}
|
|
|
|
return( m_sLocalIP );
|
|
}
|
|
|
|
CS_STRING Csock::GetRemoteIP()
|
|
{
|
|
if( !m_sRemoteIP.empty() )
|
|
return( m_sRemoteIP );
|
|
|
|
cs_sock_t iSock = GetSock();
|
|
|
|
if( iSock == CS_INVALID_SOCK )
|
|
return( "" );
|
|
|
|
struct sockaddr_storage cAddr;
|
|
socklen_t iAddrLen = sizeof( cAddr );
|
|
if( getpeername( iSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 )
|
|
{
|
|
ConvertAddress( &cAddr, iAddrLen, m_sRemoteIP, &m_iRemotePort );
|
|
}
|
|
|
|
return( m_sRemoteIP );
|
|
}
|
|
|
|
bool Csock::IsConnected() const { return( m_bIsConnected ); }
|
|
void Csock::SetIsConnected( bool b ) { m_bIsConnected = b; }
|
|
|
|
cs_sock_t & Csock::GetRSock() { return( m_iReadSock ); }
|
|
void Csock::SetRSock( cs_sock_t iSock ) { m_iReadSock = iSock; }
|
|
cs_sock_t & Csock::GetWSock() { return( m_iWriteSock ); }
|
|
void Csock::SetWSock( cs_sock_t iSock ) { m_iWriteSock = iSock; }
|
|
void Csock::SetSock( cs_sock_t iSock ) { m_iWriteSock = iSock; m_iReadSock = iSock; }
|
|
cs_sock_t & Csock::GetSock() { return( m_iReadSock ); }
|
|
void Csock::ResetTimer() { m_iLastCheckTimeoutTime = 0; m_iTcount = 0; }
|
|
void Csock::PauseRead() { m_bPauseRead = true; }
|
|
bool Csock::IsReadPaused() { return( m_bPauseRead ); }
|
|
|
|
void Csock::UnPauseRead()
|
|
{
|
|
m_bPauseRead = false;
|
|
ResetTimer();
|
|
PushBuff( "", 0, true );
|
|
}
|
|
|
|
void Csock::SetTimeout( int iTimeout, u_int iTimeoutType )
|
|
{
|
|
m_iTimeoutType = iTimeoutType;
|
|
m_iTimeout = iTimeout;
|
|
}
|
|
|
|
void Csock::CallSockError( int iErrno, const CS_STRING & sDescription )
|
|
{
|
|
if( sDescription.size() )
|
|
SockError( iErrno, sDescription );
|
|
else
|
|
{
|
|
char szBuff[0xff];
|
|
SockError( iErrno, CS_StrError( iErrno, szBuff, 0xff ) );
|
|
}
|
|
}
|
|
void Csock::SetTimeoutType( u_int iTimeoutType ) { m_iTimeoutType = iTimeoutType; }
|
|
int Csock::GetTimeout() const { return m_iTimeout; }
|
|
u_int Csock::GetTimeoutType() const { return( m_iTimeoutType ); }
|
|
|
|
bool Csock::CheckTimeout( time_t iNow )
|
|
{
|
|
if( m_iLastCheckTimeoutTime == 0 )
|
|
{
|
|
m_iLastCheckTimeoutTime = iNow;
|
|
return( false );
|
|
}
|
|
|
|
if( IsReadPaused() )
|
|
return( false );
|
|
|
|
time_t iDiff = 0;
|
|
if( iNow > m_iLastCheckTimeoutTime )
|
|
iDiff = iNow - m_iLastCheckTimeoutTime;
|
|
else
|
|
{
|
|
// this is weird, but its possible if someone changes a clock and it went back in time, this essentially has to reset the last check
|
|
// the worst case scenario is the timeout is about to it and the clock changes, it would then cause
|
|
// this to pass over the last half the time
|
|
m_iLastCheckTimeoutTime = iNow;
|
|
}
|
|
|
|
if( m_iTimeout > 0 )
|
|
{
|
|
// this is basically to help stop a clock adjust ahead, stuff could reset immediatly on a clock jump
|
|
// otherwise
|
|
time_t iRealTimeout = m_iTimeout;
|
|
if( iRealTimeout <= 1 )
|
|
m_iTcount++;
|
|
else if( m_iTcount == 0 )
|
|
iRealTimeout /= 2;
|
|
if( iDiff >= iRealTimeout )
|
|
{
|
|
if( m_iTcount == 0 )
|
|
m_iLastCheckTimeoutTime = iNow - iRealTimeout;
|
|
if( m_iTcount++ >= 1 )
|
|
{
|
|
Timeout();
|
|
return( true );
|
|
}
|
|
}
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
void Csock::PushBuff( const char *data, size_t len, bool bStartAtZero )
|
|
{
|
|
if( !m_bEnableReadLine )
|
|
return; // If the ReadLine event is disabled, just ditch here
|
|
|
|
size_t iStartPos = ( m_sbuffer.empty() || bStartAtZero ? 0 : m_sbuffer.length() - 1 );
|
|
|
|
if( data )
|
|
m_sbuffer.append( data, len );
|
|
|
|
while( !m_bPauseRead && GetCloseType() == CLT_DONT )
|
|
{
|
|
CS_STRING::size_type iFind = m_sbuffer.find( "\n", iStartPos );
|
|
|
|
if( iFind != CS_STRING::npos )
|
|
{
|
|
CS_STRING sBuff = m_sbuffer.substr( 0, iFind + 1 ); // read up to(including) the newline
|
|
m_sbuffer.erase( 0, iFind + 1 ); // erase past the newline
|
|
ReadLine( sBuff );
|
|
iStartPos = 0; // reset this back to 0, since we need to look for the next newline here.
|
|
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if( m_iMaxStoredBufferLength > 0 && m_sbuffer.length() > m_iMaxStoredBufferLength )
|
|
ReachedMaxBuffer(); // call the max read buffer event
|
|
|
|
}
|
|
|
|
CS_STRING & Csock::GetInternalReadBuffer() { return( m_sbuffer ); }
|
|
CS_STRING & Csock::GetInternalWriteBuffer() { return( m_sSend ); }
|
|
void Csock::SetMaxBufferThreshold( u_int iThreshold ) { m_iMaxStoredBufferLength = iThreshold; }
|
|
u_int Csock::GetMaxBufferThreshold() const { return( m_iMaxStoredBufferLength ); }
|
|
int Csock::GetType() const { return( m_iConnType ); }
|
|
void Csock::SetType( int iType ) { m_iConnType = iType; }
|
|
const CS_STRING & Csock::GetSockName() const { return( m_sSockName ); }
|
|
void Csock::SetSockName( const CS_STRING & sName ) { m_sSockName = sName; }
|
|
const CS_STRING & Csock::GetHostName() const { return( m_shostname ); }
|
|
void Csock::SetHostName( const CS_STRING & sHostname ) { m_shostname = sHostname; }
|
|
unsigned long long Csock::GetStartTime() const { return( m_iStartTime ); }
|
|
void Csock::ResetStartTime() { m_iStartTime = 0; }
|
|
unsigned long long Csock::GetBytesRead() const { return( m_iBytesRead ); }
|
|
void Csock::ResetBytesRead() { m_iBytesRead = 0; }
|
|
unsigned long long Csock::GetBytesWritten() const { return( m_iBytesWritten ); }
|
|
void Csock::ResetBytesWritten() { m_iBytesWritten = 0; }
|
|
|
|
double Csock::GetAvgRead( unsigned long long iSample )
|
|
{
|
|
unsigned long long iDifference = ( millitime() - m_iStartTime );
|
|
|
|
if( m_iBytesRead == 0 || iSample > iDifference )
|
|
return(( double )m_iBytesRead );
|
|
|
|
return((( double )m_iBytesRead / (( double )iDifference / ( double )iSample ) ) );
|
|
}
|
|
|
|
double Csock::GetAvgWrite( unsigned long long iSample )
|
|
{
|
|
unsigned long long iDifference = ( millitime() - m_iStartTime );
|
|
|
|
if( m_iBytesWritten == 0 || iSample > iDifference )
|
|
return(( double )m_iBytesWritten );
|
|
|
|
return((( double )m_iBytesWritten / (( double )iDifference / ( double )iSample ) ) );
|
|
}
|
|
|
|
u_short Csock::GetRemotePort()
|
|
{
|
|
if( m_iRemotePort > 0 )
|
|
return( m_iRemotePort );
|
|
GetRemoteIP();
|
|
return( m_iRemotePort );
|
|
}
|
|
|
|
u_short Csock::GetLocalPort()
|
|
{
|
|
if( m_iLocalPort > 0 )
|
|
return( m_iLocalPort );
|
|
GetLocalIP();
|
|
return( m_iLocalPort );
|
|
}
|
|
|
|
u_short Csock::GetPort() { return( m_uPort ); }
|
|
void Csock::SetPort( u_short iPort ) { m_uPort = iPort; }
|
|
void Csock::Close( ECloseType eCloseType )
|
|
{
|
|
m_eCloseType = eCloseType;
|
|
}
|
|
|
|
void Csock::NonBlockingIO()
|
|
{
|
|
set_non_blocking( m_iReadSock );
|
|
|
|
if( m_iReadSock != m_iWriteSock )
|
|
{
|
|
set_non_blocking( m_iWriteSock );
|
|
}
|
|
}
|
|
|
|
bool Csock::GetSSL() { return( m_bUseSSL ); }
|
|
void Csock::SetSSL( bool b ) { m_bUseSSL = b; }
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
void Csock::SetCipher( const CS_STRING & sCipher ) { m_sCipherType = sCipher; }
|
|
const CS_STRING & Csock::GetCipher() { return( m_sCipherType ); }
|
|
void Csock::SetPemLocation( const CS_STRING & sPemFile ) { m_sPemFile = sPemFile; }
|
|
const CS_STRING & Csock::GetPemLocation() { return( m_sPemFile ); }
|
|
void Csock::SetPemPass( const CS_STRING & sPassword ) { m_sPemPass = sPassword; }
|
|
const CS_STRING & Csock::GetPemPass() const { return( m_sPemPass ); }
|
|
|
|
int Csock::PemPassCB( char *buf, int size, int rwflag, void *pcSocket )
|
|
{
|
|
Csock *pSock = ( Csock * )pcSocket;
|
|
const CS_STRING & sPassword = pSock->GetPemPass();
|
|
memset( buf, '\0', size );
|
|
strncpy( buf, sPassword.c_str(), size );
|
|
buf[size-1] = '\0';
|
|
return(( int )strlen( buf ) );
|
|
}
|
|
|
|
int Csock::CertVerifyCB( int preverify_ok, X509_STORE_CTX *x509_ctx )
|
|
{
|
|
/*
|
|
* A small quick example on how to get ahold of the Csock in the data portion of x509_ctx
|
|
Csock *pSock = GetCsockFromCTX( x509_ctx );
|
|
assert( pSock );
|
|
cerr << pSock->GetRemoteIP() << endl;
|
|
*/
|
|
|
|
/* return 1 always for now, probably want to add some code for cert verification */
|
|
return( 1 );
|
|
}
|
|
|
|
void Csock::SetSSLMethod( int iMethod ) { m_iMethod = iMethod; }
|
|
int Csock::GetSSLMethod() { return( m_iMethod ); }
|
|
void Csock::SetSSLObject( SSL *ssl ) { m_ssl = ssl; }
|
|
void Csock::SetCTXObject( SSL_CTX *sslCtx ) { m_ssl_ctx = sslCtx; }
|
|
|
|
SSL_SESSION * Csock::GetSSLSession()
|
|
{
|
|
if( m_ssl )
|
|
return( SSL_get_session( m_ssl ) );
|
|
|
|
return( NULL );
|
|
}
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
const CS_STRING & Csock::GetWriteBuffer() { return( m_sSend ); }
|
|
void Csock::ClearWriteBuffer() { m_sSend.clear(); }
|
|
bool Csock::SslIsEstablished() { return ( m_bsslEstablished ); }
|
|
|
|
bool Csock::ConnectInetd( bool bIsSSL, const CS_STRING & sHostname )
|
|
{
|
|
if( !sHostname.empty() )
|
|
m_sSockName = sHostname;
|
|
|
|
// set our hostname
|
|
if( m_sSockName.empty() )
|
|
{
|
|
struct sockaddr_in client;
|
|
socklen_t clen = sizeof( client );
|
|
if( getpeername( 0, ( struct sockaddr * )&client, &clen ) < 0 )
|
|
m_sSockName = "0.0.0.0:0";
|
|
else
|
|
{
|
|
stringstream s;
|
|
s << inet_ntoa( client.sin_addr ) << ":" << ntohs( client.sin_port );
|
|
m_sSockName = s.str();
|
|
}
|
|
}
|
|
|
|
return( ConnectFD( 0, 1, m_sSockName, bIsSSL, INBOUND ) );
|
|
}
|
|
|
|
bool Csock::ConnectFD( int iReadFD, int iWriteFD, const CS_STRING & sName, bool bIsSSL, ETConn eDirection )
|
|
{
|
|
if( eDirection == LISTENER )
|
|
{
|
|
CS_DEBUG( "You can not use a LISTENER type here!" );
|
|
return( false );
|
|
}
|
|
|
|
// set our socket type
|
|
SetType( eDirection );
|
|
|
|
// set the hostname
|
|
m_sSockName = sName;
|
|
|
|
// set the file descriptors
|
|
SetRSock( iReadFD );
|
|
SetWSock( iWriteFD );
|
|
|
|
// set it up as non-blocking io
|
|
NonBlockingIO();
|
|
|
|
if( bIsSSL )
|
|
{
|
|
if( eDirection == INBOUND && !AcceptSSL() )
|
|
return( false );
|
|
else if( eDirection == OUTBOUND && !ConnectSSL() )
|
|
return( false );
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
X509 *Csock::GetX509()
|
|
{
|
|
if( m_ssl )
|
|
return( SSL_get_peer_certificate( m_ssl ) );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
CS_STRING Csock::GetPeerPubKey()
|
|
{
|
|
CS_STRING sKey;
|
|
|
|
SSL_SESSION *pSession = GetSSLSession();
|
|
|
|
if( pSession && pSession->peer )
|
|
{
|
|
EVP_PKEY *pKey = X509_get_pubkey( pSession->peer );
|
|
if( pKey )
|
|
{
|
|
char *hxKey = NULL;
|
|
switch( pKey->type )
|
|
{
|
|
case EVP_PKEY_RSA:
|
|
{
|
|
hxKey = BN_bn2hex( pKey->pkey.rsa->n );
|
|
break;
|
|
}
|
|
case EVP_PKEY_DSA:
|
|
{
|
|
hxKey = BN_bn2hex( pKey->pkey.dsa->pub_key );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
CS_DEBUG( "Not Prepared for Public Key Type [" << pKey->type << "]" );
|
|
break;
|
|
}
|
|
}
|
|
if( hxKey )
|
|
{
|
|
sKey = hxKey;
|
|
OPENSSL_free( hxKey );
|
|
}
|
|
EVP_PKEY_free( pKey );
|
|
}
|
|
}
|
|
return( sKey );
|
|
}
|
|
long Csock::GetPeerFingerprint( CS_STRING & sFP )
|
|
{
|
|
sFP.clear();
|
|
|
|
if( !m_ssl )
|
|
return( 0 );
|
|
|
|
X509* pCert = GetX509();
|
|
|
|
// Inspired by charybdis
|
|
if( pCert )
|
|
{
|
|
for( int i = 0; i < SHA_DIGEST_LENGTH; i++ )
|
|
{
|
|
char buf[3];
|
|
snprintf( buf, 3, "%02x", pCert->sha1_hash[i] );
|
|
sFP += buf;
|
|
}
|
|
X509_free( pCert );
|
|
}
|
|
|
|
return( SSL_get_verify_result( m_ssl ) );
|
|
}
|
|
unsigned int Csock::GetRequireClientCertFlags() { return( m_iRequireClientCertFlags ); }
|
|
void Csock::SetRequiresClientCert( bool bRequiresCert ) { m_iRequireClientCertFlags = ( bRequiresCert ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT|SSL_VERIFY_PEER : 0 ); }
|
|
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
void Csock::SetParentSockName( const CS_STRING & sParentName ) { m_sParentName = sParentName; }
|
|
const CS_STRING & Csock::GetParentSockName() { return( m_sParentName ); }
|
|
|
|
void Csock::SetRate( u_int iBytes, unsigned long long iMilliseconds )
|
|
{
|
|
m_iMaxBytes = iBytes;
|
|
m_iMaxMilliSeconds = iMilliseconds;
|
|
}
|
|
|
|
u_int Csock::GetRateBytes() { return( m_iMaxBytes ); }
|
|
unsigned long long Csock::GetRateTime() { return( m_iMaxMilliSeconds ); }
|
|
|
|
|
|
void Csock::EnableReadLine() { m_bEnableReadLine = true; }
|
|
void Csock::DisableReadLine()
|
|
{
|
|
m_bEnableReadLine = false;
|
|
m_sbuffer.clear();
|
|
}
|
|
|
|
void Csock::ReachedMaxBuffer()
|
|
{
|
|
std::cerr << "Warning, Max Buffer length Warning Threshold has been hit" << endl;
|
|
std::cerr << "If you don't care, then set SetMaxBufferThreshold to 0" << endl;
|
|
}
|
|
|
|
time_t Csock::GetTimeSinceLastDataTransaction( time_t iNow )
|
|
{
|
|
if( m_iLastCheckTimeoutTime == 0 )
|
|
return( 0 );
|
|
return(( iNow > 0 ? iNow : time( NULL ) ) - m_iLastCheckTimeoutTime );
|
|
}
|
|
|
|
time_t Csock::GetNextCheckTimeout( time_t iNow )
|
|
{
|
|
if( iNow == 0 )
|
|
iNow = time( NULL );
|
|
time_t iTimeout = m_iTimeout;
|
|
time_t iDiff = iNow - m_iLastCheckTimeoutTime;
|
|
/* CheckTimeout() wants to be called after half the timeout */
|
|
if( m_iTcount == 0 )
|
|
iTimeout /= 2;
|
|
if( iDiff > iTimeout )
|
|
iTimeout = 0;
|
|
else
|
|
iTimeout -= iDiff;
|
|
return( iNow + iTimeout );
|
|
}
|
|
|
|
int Csock::GetPending()
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
if( m_ssl )
|
|
{
|
|
// in v23 method, the pending function is initialized to ssl_undefined_const_function
|
|
// which throws SSL_UNDEFINED_CONST_FUNCTION on to the error stack
|
|
// this is one of the more stupid things in openssl, it seems bizarre that even though SSL_pending
|
|
// returns an int, they don't bother returning in error to notify us, so basically
|
|
// we have to always clear errors here generated by SSL_pending, otherwise the stack could
|
|
// have a lame error on it causing SSL_write to fail in certain instances.
|
|
#if defined( OPENSSL_VERSION_NUMBER ) && OPENSSL_VERSION_NUMBER >= 0x00908000
|
|
ERR_set_mark();
|
|
int iBytes = SSL_pending( m_ssl );
|
|
ERR_pop_to_mark();
|
|
return( iBytes );
|
|
#else
|
|
int iBytes = SSL_pending( m_ssl );
|
|
ERR_clear_error(); // to get safer handling, upgrade your openssl version!
|
|
return( iBytes );
|
|
#endif /* OPENSSL_VERSION_NUMBER */
|
|
}
|
|
else
|
|
return( 0 );
|
|
#else
|
|
return( 0 );
|
|
#endif /* HAVE_LIBSSL */
|
|
}
|
|
|
|
bool Csock::CreateSocksFD()
|
|
{
|
|
if( m_iReadSock != CS_INVALID_SOCK )
|
|
return( true );
|
|
|
|
m_iReadSock = m_iWriteSock = CreateSocket();
|
|
if( m_iReadSock == CS_INVALID_SOCK )
|
|
return( false );
|
|
|
|
m_address.SinFamily();
|
|
m_address.SinPort( m_uPort );
|
|
|
|
return( true );
|
|
}
|
|
|
|
|
|
int Csock::GetAddrInfo( const CS_STRING & sHostname, CSSockAddr & csSockAddr )
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
if( csSockAddr.GetAFRequire() != AF_INET && inet_pton( AF_INET6, sHostname.c_str(), csSockAddr.GetAddr6() ) > 0 )
|
|
{
|
|
SetIPv6( true );
|
|
return( 0 );
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
if( inet_pton( AF_INET, sHostname.c_str(), csSockAddr.GetAddr() ) > 0 )
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
SetIPv6( false );
|
|
#endif /* HAVE_IPV6 */
|
|
return( 0 );
|
|
}
|
|
|
|
#ifdef HAVE_C_ARES
|
|
// need to compute this up here
|
|
if( !m_pARESChannel )
|
|
{
|
|
if( ares_init( &m_pARESChannel ) != ARES_SUCCESS )
|
|
{
|
|
// TODO throw some debug?
|
|
FreeAres();
|
|
return( ETIMEDOUT );
|
|
}
|
|
m_pCurrAddr = &csSockAddr; // flag its starting
|
|
|
|
int iFamily = AF_INET;
|
|
#ifdef HAVE_IPV6
|
|
#if ARES_VERSION >= CREATE_ARES_VER( 1, 7, 5 )
|
|
// as of ares 1.7.5, it falls back to af_inet only when AF_UNSPEC is specified
|
|
// so this can finally let the code flow through as anticipated :)
|
|
iFamily = csSockAddr.GetAFRequire();
|
|
#else
|
|
// as of ares 1.6.0 if it fails on af_inet6, it falls back to af_inet,
|
|
// this code was here in the previous Csocket version, just adding the comment as a reminder
|
|
iFamily = csSockAddr.GetAFRequire() == CSSockAddr::RAF_ANY ? AF_INET6 : csSockAddr.GetAFRequire();
|
|
#endif /* CREATE_ARES_VER( 1, 7, 5 ) */
|
|
#endif /* HAVE_IPV6 */
|
|
ares_gethostbyname( m_pARESChannel, sHostname.c_str(), iFamily, AresHostCallback, this );
|
|
}
|
|
if( !m_pCurrAddr )
|
|
{
|
|
// this means its finished
|
|
FreeAres();
|
|
#ifdef HAVE_IPV6
|
|
if( GetType() != LISTENER && m_iARESStatus == ARES_SUCCESS && csSockAddr.GetAFRequire() == CSSockAddr::RAF_ANY && GetIPv6() )
|
|
{
|
|
// this means that ares_host returned an ipv6 host, so try a connect right away
|
|
if( CreateSocksFD() && Connect() )
|
|
{
|
|
SetSkipConnect( true );
|
|
}
|
|
#ifndef _WIN32
|
|
else if( GetSockError() == ENETUNREACH )
|
|
#else
|
|
else if( GetSockError() == WSAENETUNREACH || GetSockError() == WSAEHOSTUNREACH )
|
|
#endif /* !_WIN32 */
|
|
{
|
|
// the Connect() failed, so throw a retry back in with ipv4, and let it process normally
|
|
CS_DEBUG( "Failed ipv6 connection with PF_UNSPEC, falling back to ipv4" );
|
|
m_iARESStatus = -1;
|
|
CloseSocksFD();
|
|
SetAFRequire( CSSockAddr::RAF_INET );
|
|
return( GetAddrInfo( sHostname, csSockAddr ) );
|
|
}
|
|
}
|
|
#if ARES_VERSION < CREATE_ARES_VER( 1, 5, 3 )
|
|
if( m_iARESStatus != ARES_SUCCESS && csSockAddr.GetAFRequire() == CSSockAddr::RAF_ANY )
|
|
{
|
|
// this is a workaround for ares < 1.5.3 where the builtin retry on failed AF_INET6 isn't there yet
|
|
CS_DEBUG( "Retry for older version of c-ares with AF_INET only" );
|
|
// this means we tried previously with AF_INET6 and failed, so force AF_INET and retry
|
|
SetAFRequire( CSSockAddr::RAF_INET );
|
|
return( GetAddrInfo( sHostname, csSockAddr ) );
|
|
}
|
|
#endif /* ARES_VERSION < CREATE_ARES_VER( 1, 5, 3 ) */
|
|
#endif /* HAVE_IPV6 */
|
|
return( m_iARESStatus == ARES_SUCCESS ? 0 : ETIMEDOUT );
|
|
}
|
|
return( EAGAIN );
|
|
#else /* HAVE_C_ARES */
|
|
return( ::GetAddrInfo( sHostname, this, csSockAddr ) );
|
|
#endif /* HAVE_C_ARES */
|
|
}
|
|
|
|
int Csock::DNSLookup( EDNSLType eDNSLType )
|
|
{
|
|
if( eDNSLType == DNS_VHOST )
|
|
{
|
|
if( m_sBindHost.empty() )
|
|
{
|
|
if( m_eConState != CST_OK )
|
|
m_eConState = CST_DESTDNS; // skip binding, there is no vhost
|
|
return( 0 );
|
|
}
|
|
|
|
m_bindhost.SinFamily();
|
|
m_bindhost.SinPort( 0 );
|
|
}
|
|
|
|
int iRet = ETIMEDOUT;
|
|
if( eDNSLType == DNS_VHOST )
|
|
{
|
|
iRet = GetAddrInfo( m_sBindHost, m_bindhost );
|
|
#ifdef HAVE_IPV6
|
|
if( m_bindhost.GetIPv6() )
|
|
{
|
|
SetAFRequire( CSSockAddr::RAF_INET6 );
|
|
}
|
|
else
|
|
{
|
|
SetAFRequire( CSSockAddr::RAF_INET );
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
else
|
|
{
|
|
iRet = GetAddrInfo( m_shostname, m_address );
|
|
}
|
|
|
|
if( iRet == 0 )
|
|
{
|
|
if( !CreateSocksFD() )
|
|
{
|
|
m_iDNSTryCount = 0;
|
|
return( ETIMEDOUT );
|
|
}
|
|
if( m_eConState != CST_OK )
|
|
m_eConState = (( eDNSLType == DNS_VHOST ) ? CST_BINDVHOST : CST_CONNECT );
|
|
m_iDNSTryCount = 0;
|
|
return( 0 );
|
|
}
|
|
else if( iRet == EAGAIN )
|
|
{
|
|
#ifndef HAVE_C_ARES
|
|
m_iDNSTryCount++;
|
|
if( m_iDNSTryCount > 20 )
|
|
{
|
|
m_iDNSTryCount = 0;
|
|
return( ETIMEDOUT );
|
|
}
|
|
#endif /* HAVE_C_ARES */
|
|
return( EAGAIN );
|
|
}
|
|
m_iDNSTryCount = 0;
|
|
return( ETIMEDOUT );
|
|
}
|
|
|
|
bool Csock::SetupVHost()
|
|
{
|
|
if( m_sBindHost.empty() )
|
|
{
|
|
if( m_eConState != CST_OK )
|
|
m_eConState = CST_DESTDNS;
|
|
return( true );
|
|
}
|
|
int iRet = -1;
|
|
if( !GetIPv6() )
|
|
iRet = bind( m_iReadSock, ( struct sockaddr * ) m_bindhost.GetSockAddr(), m_bindhost.GetSockAddrLen() );
|
|
#ifdef HAVE_IPV6
|
|
else
|
|
iRet = bind( m_iReadSock, ( struct sockaddr * ) m_bindhost.GetSockAddr6(), m_bindhost.GetSockAddrLen6() );
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
if( iRet == 0 )
|
|
{
|
|
if( m_eConState != CST_OK )
|
|
m_eConState = CST_DESTDNS;
|
|
return( true );
|
|
}
|
|
m_iCurBindCount++;
|
|
if( m_iCurBindCount > 3 )
|
|
{
|
|
CS_DEBUG( "Failure to bind to " << m_sBindHost );
|
|
return( false );
|
|
}
|
|
|
|
return( true );
|
|
}
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
void Csock::FREE_SSL()
|
|
{
|
|
if( m_ssl )
|
|
{
|
|
SSL_shutdown( m_ssl );
|
|
SSL_free( m_ssl );
|
|
}
|
|
m_ssl = NULL;
|
|
}
|
|
|
|
void Csock::FREE_CTX()
|
|
{
|
|
if( m_ssl_ctx )
|
|
SSL_CTX_free( m_ssl_ctx );
|
|
|
|
m_ssl_ctx = NULL;
|
|
}
|
|
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
cs_sock_t Csock::CreateSocket( bool bListen )
|
|
{
|
|
#ifdef HAVE_IPV6
|
|
cs_sock_t iRet = socket(( GetIPv6() ? PF_INET6 : PF_INET ), SOCK_STREAM, IPPROTO_TCP );
|
|
#else
|
|
cs_sock_t iRet = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
if( iRet != CS_INVALID_SOCK )
|
|
{
|
|
set_close_on_exec( iRet );
|
|
|
|
if( bListen )
|
|
{
|
|
const int on = 1;
|
|
|
|
if( setsockopt( iRet, SOL_SOCKET, SO_REUSEADDR, ( char * )&on, sizeof( on ) ) != 0 )
|
|
PERROR( "SO_REUSEADDR" );
|
|
}
|
|
}
|
|
else
|
|
PERROR( "socket" );
|
|
|
|
return( iRet );
|
|
}
|
|
|
|
void Csock::Init( const CS_STRING & sHostname, u_short uPort, int iTimeout )
|
|
{
|
|
#ifdef HAVE_LIBSSL
|
|
m_ssl = NULL;
|
|
m_ssl_ctx = NULL;
|
|
m_iRequireClientCertFlags = 0;
|
|
#endif /* HAVE_LIBSSL */
|
|
m_iTcount = 0;
|
|
m_iReadSock = CS_INVALID_SOCK;
|
|
m_iWriteSock = CS_INVALID_SOCK;
|
|
m_iTimeout = iTimeout;
|
|
m_iMaxConns = SOMAXCONN;
|
|
m_bUseSSL = false;
|
|
m_bIsConnected = false;
|
|
m_uPort = uPort;
|
|
m_shostname = sHostname;
|
|
m_sbuffer.clear();
|
|
m_eCloseType = CLT_DONT;
|
|
m_iMethod = SSL23;
|
|
m_sCipherType = "ALL";
|
|
m_iMaxBytes = 0;
|
|
m_iMaxMilliSeconds = 0;
|
|
m_iLastSendTime = 0;
|
|
m_iLastSend = 0;
|
|
m_bsslEstablished = false;
|
|
m_bEnableReadLine = false;
|
|
m_iMaxStoredBufferLength = 1024;
|
|
m_iConnType = INBOUND;
|
|
m_iRemotePort = 0;
|
|
m_iLocalPort = 0;
|
|
m_iBytesRead = 0;
|
|
m_iBytesWritten = 0;
|
|
m_iStartTime = millitime();
|
|
m_bPauseRead = false;
|
|
m_iTimeoutType = TMO_ALL;
|
|
m_eConState = CST_OK; // default should be ok
|
|
m_iDNSTryCount = 0;
|
|
m_iCurBindCount = 0;
|
|
m_bIsIPv6 = false;
|
|
m_bSkipConnect = false;
|
|
m_iLastCheckTimeoutTime = 0;
|
|
#ifdef HAVE_C_ARES
|
|
m_pARESChannel = NULL;
|
|
m_pCurrAddr = NULL;
|
|
m_iARESStatus = -1;
|
|
#endif /* HAVE_C_ARES */
|
|
}
|
|
|
|
////////////////////////// CSocketManager //////////////////////////
|
|
CSocketManager::CSocketManager() : std::vector<Csock *>(), CSockCommon()
|
|
{
|
|
m_errno = SUCCESS;
|
|
m_iCallTimeouts = millitime();
|
|
m_iSelectWait = 100000; // Default of 100 milliseconds
|
|
m_iBytesRead = 0;
|
|
m_iBytesWritten = 0;
|
|
}
|
|
|
|
CSocketManager::~CSocketManager()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void CSocketManager::clear()
|
|
{
|
|
while( this->size() )
|
|
DelSock( 0 );
|
|
}
|
|
|
|
void CSocketManager::Cleanup()
|
|
{
|
|
CleanupCrons();
|
|
CleanupFDMonitors();
|
|
clear();
|
|
}
|
|
|
|
Csock * CSocketManager::GetSockObj( const CS_STRING & sHostname, u_short uPort, int iTimeout )
|
|
{
|
|
return( new Csock( sHostname, uPort, iTimeout ) );
|
|
}
|
|
|
|
void CSocketManager::Connect( const CSConnection & cCon, Csock * pcSock )
|
|
{
|
|
// create the new object
|
|
if( !pcSock )
|
|
pcSock = GetSockObj( cCon.GetHostname(), cCon.GetPort(), cCon.GetTimeout() );
|
|
else
|
|
{
|
|
pcSock->SetHostName( cCon.GetHostname() );
|
|
pcSock->SetPort( cCon.GetPort() );
|
|
pcSock->SetTimeout( cCon.GetTimeout() );
|
|
}
|
|
|
|
if( cCon.GetAFRequire() != CSSockAddr::RAF_ANY )
|
|
pcSock->SetAFRequire( cCon.GetAFRequire() );
|
|
|
|
// bind the vhost
|
|
pcSock->SetBindHost( cCon.GetBindHost() );
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
pcSock->SetSSL( cCon.GetIsSSL() );
|
|
if( cCon.GetIsSSL() )
|
|
{
|
|
if( !cCon.GetPemLocation().empty() )
|
|
{
|
|
pcSock->SetPemLocation( cCon.GetPemLocation() );
|
|
pcSock->SetPemPass( cCon.GetPemPass() );
|
|
}
|
|
if( !cCon.GetCipher().empty() )
|
|
pcSock->SetCipher( cCon.GetCipher() );
|
|
}
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
pcSock->SetType( Csock::OUTBOUND );
|
|
|
|
pcSock->SetConState( Csock::CST_START );
|
|
AddSock( pcSock, cCon.GetSockName() );
|
|
}
|
|
|
|
bool CSocketManager::Listen( const CSListener & cListen, Csock * pcSock, u_short *piRandPort )
|
|
{
|
|
if( !pcSock )
|
|
pcSock = GetSockObj( "", 0 );
|
|
|
|
if( cListen.GetAFRequire() != CSSockAddr::RAF_ANY )
|
|
{
|
|
pcSock->SetAFRequire( cListen.GetAFRequire() );
|
|
#ifdef HAVE_IPV6
|
|
if( cListen.GetAFRequire() == CSSockAddr::RAF_INET6 )
|
|
pcSock->SetIPv6( true );
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
#ifdef HAVE_IPV6
|
|
else
|
|
{
|
|
pcSock->SetIPv6( true );
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
#ifdef HAVE_LIBSSL
|
|
pcSock->SetSSL( cListen.GetIsSSL() );
|
|
if( cListen.GetIsSSL() && !cListen.GetPemLocation().empty() )
|
|
{
|
|
pcSock->SetPemLocation( cListen.GetPemLocation() );
|
|
pcSock->SetPemPass( cListen.GetPemPass() );
|
|
pcSock->SetCipher( cListen.GetCipher() );
|
|
pcSock->SetRequireClientCertFlags( cListen.GetRequireClientCertFlags() );
|
|
}
|
|
#endif /* HAVE_LIBSSL */
|
|
|
|
if( piRandPort )
|
|
*piRandPort = 0;
|
|
|
|
bool bDetach = ( cListen.GetDetach() && !piRandPort ); // can't detach if we're waiting for the port to come up right now
|
|
|
|
if( pcSock->Listen( cListen.GetPort(), cListen.GetMaxConns(), cListen.GetBindHost(), cListen.GetTimeout(), bDetach ) )
|
|
{
|
|
AddSock( pcSock, cListen.GetSockName() );
|
|
if( piRandPort && cListen.GetPort() == 0 )
|
|
{
|
|
cs_sock_t iSock = pcSock->GetSock();
|
|
|
|
if( iSock == CS_INVALID_SOCK )
|
|
{
|
|
CS_DEBUG( "Failed to attain a valid file descriptor" );
|
|
pcSock->Close();
|
|
return( false );
|
|
}
|
|
struct sockaddr_in mLocalAddr;
|
|
socklen_t mLocalLen = sizeof( mLocalAddr );
|
|
getsockname( iSock, ( struct sockaddr * ) &mLocalAddr, &mLocalLen );
|
|
*piRandPort = ntohs( mLocalAddr.sin_port );
|
|
}
|
|
return( true );
|
|
}
|
|
|
|
CS_Delete( pcSock );
|
|
return( false );
|
|
}
|
|
|
|
|
|
bool CSocketManager::HasFDs() const
|
|
{
|
|
return( this->size() || m_vcMonitorFD.size() );
|
|
}
|
|
|
|
void CSocketManager::Loop()
|
|
{
|
|
for( u_int a = 0; a < this->size(); a++ )
|
|
{
|
|
Csock *pcSock = ( *this )[a];
|
|
|
|
if( pcSock->GetType() != Csock::OUTBOUND || pcSock->GetConState() == Csock::CST_OK )
|
|
continue;
|
|
if( pcSock->GetConState() == Csock::CST_DNS )
|
|
{
|
|
if( pcSock->DNSLookup( Csock::DNS_VHOST ) == ETIMEDOUT )
|
|
{
|
|
pcSock->CallSockError( EDOM, "DNS Lookup for bind host failed" );
|
|
DelSock( a-- );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( pcSock->GetConState() == Csock::CST_BINDVHOST )
|
|
{
|
|
if( !pcSock->SetupVHost() )
|
|
{
|
|
pcSock->CallSockError( GetSockError(), "Failed to setup bind host" );
|
|
DelSock( a-- );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( pcSock->GetConState() == Csock::CST_DESTDNS )
|
|
{
|
|
if( pcSock->DNSLookup( Csock::DNS_DEST ) == ETIMEDOUT )
|
|
{
|
|
pcSock->CallSockError( EADDRNOTAVAIL, "Unable to resolve requested address" );
|
|
DelSock( a-- );
|
|
continue;
|
|
}
|
|
}
|
|
if( pcSock->GetConState() == Csock::CST_CONNECT )
|
|
{
|
|
if( !pcSock->Connect() )
|
|
{
|
|
if( GetSockError() == ECONNREFUSED )
|
|
pcSock->ConnectionRefused();
|
|
else
|
|
pcSock->CallSockError( GetSockError() );
|
|
|
|
DelSock( a-- );
|
|
continue;
|
|
}
|
|
}
|
|
#ifdef HAVE_LIBSSL
|
|
if( pcSock->GetConState() == Csock::CST_CONNECTSSL )
|
|
{
|
|
if( pcSock->GetSSL() )
|
|
{
|
|
if( !pcSock->ConnectSSL() )
|
|
{
|
|
if( GetSockError() == ECONNREFUSED )
|
|
pcSock->ConnectionRefused();
|
|
else
|
|
pcSock->CallSockError( GetSockError() == 0 ? ECONNABORTED : GetSockError() );
|
|
|
|
DelSock( a-- );
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_LIBSSL */
|
|
}
|
|
|
|
std::map<Csock *, EMessages> mpeSocks;
|
|
Select( mpeSocks );
|
|
|
|
switch( m_errno )
|
|
{
|
|
case SUCCESS:
|
|
{
|
|
for( std::map<Csock *, EMessages>::iterator itSock = mpeSocks.begin(); itSock != mpeSocks.end(); itSock++ )
|
|
{
|
|
Csock * pcSock = itSock->first;
|
|
EMessages iErrno = itSock->second;
|
|
|
|
if( iErrno == SUCCESS )
|
|
{
|
|
// read in data
|
|
// if this is a
|
|
int iLen = 0;
|
|
|
|
if( pcSock->GetSSL() )
|
|
iLen = pcSock->GetPending();
|
|
|
|
if( iLen <= 0 )
|
|
iLen = CS_BLOCKSIZE;
|
|
|
|
CSCharBuffer cBuff( iLen );
|
|
|
|
cs_ssize_t bytes = pcSock->Read( cBuff(), iLen );
|
|
|
|
if( bytes != Csock::READ_TIMEDOUT && bytes != Csock::READ_CONNREFUSED && bytes != Csock::READ_ERR && !pcSock->IsConnected() )
|
|
{
|
|
pcSock->SetIsConnected( true );
|
|
pcSock->Connected();
|
|
}
|
|
|
|
switch( bytes )
|
|
{
|
|
case Csock::READ_EOF:
|
|
{
|
|
DelSockByAddr( pcSock );
|
|
break;
|
|
}
|
|
|
|
case Csock::READ_ERR:
|
|
{
|
|
pcSock->CallSockError( GetSockError() );
|
|
DelSockByAddr( pcSock );
|
|
break;
|
|
}
|
|
|
|
case Csock::READ_EAGAIN:
|
|
break;
|
|
|
|
case Csock::READ_CONNREFUSED:
|
|
pcSock->ConnectionRefused();
|
|
DelSockByAddr( pcSock );
|
|
break;
|
|
|
|
case Csock::READ_TIMEDOUT:
|
|
pcSock->Timeout();
|
|
DelSockByAddr( pcSock );
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if( Csock::TMO_READ & pcSock->GetTimeoutType() )
|
|
pcSock->ResetTimer(); // reset the timeout timer
|
|
|
|
pcSock->ReadData( cBuff(), bytes ); // Call ReadData() before PushBuff() so that it is called before the ReadLine() event - LD 07/18/05
|
|
pcSock->PushBuff( cBuff(), bytes );
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if( iErrno == SELECT_ERROR )
|
|
{
|
|
// a socket came back with an error
|
|
// usually means it was closed
|
|
DelSockByAddr( pcSock );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SELECT_TIMEOUT:
|
|
case SELECT_TRYAGAIN:
|
|
case SELECT_ERROR:
|
|
default :
|
|
break;
|
|
}
|
|
|
|
unsigned long long iMilliNow = millitime();
|
|
if(( iMilliNow - m_iCallTimeouts ) >= 1000 )
|
|
{
|
|
m_iCallTimeouts = iMilliNow;
|
|
// call timeout on all the sockets that recieved no data
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
{
|
|
if(( *this )[i]->GetConState() != Csock::CST_OK )
|
|
continue;
|
|
|
|
if(( *this )[i]->CheckTimeout(( time_t )( iMilliNow / 1000 ) ) )
|
|
DelSock( i-- );
|
|
}
|
|
}
|
|
// run any Manager Crons we may have
|
|
Cron();
|
|
}
|
|
|
|
void CSocketManager::DynamicSelectLoop( u_long iLowerBounds, u_long iUpperBounds, time_t iMaxResolution )
|
|
{
|
|
SetSelectTimeout( iLowerBounds );
|
|
if( m_errno == SELECT_TIMEOUT )
|
|
{
|
|
// only do this if the previous call to select was a timeout
|
|
timeval tMaxResolution;
|
|
timeval tNow;
|
|
tMaxResolution.tv_sec = iMaxResolution;
|
|
tMaxResolution.tv_usec = 0;
|
|
gettimeofday( &tNow, NULL );
|
|
timeval tSelectTimeout = GetDynamicSleepTime( tNow, tMaxResolution );
|
|
u_long iSelectTimeout = tSelectTimeout.tv_sec * 1000000 + tSelectTimeout.tv_usec;
|
|
iSelectTimeout = std::max( iLowerBounds, iSelectTimeout );
|
|
iSelectTimeout = std::min( iSelectTimeout, iUpperBounds );
|
|
if( iLowerBounds != iSelectTimeout )
|
|
SetSelectTimeout( iSelectTimeout );
|
|
}
|
|
Loop();
|
|
}
|
|
|
|
void CSocketManager::AddSock( Csock *pcSock, const CS_STRING & sSockName )
|
|
{
|
|
pcSock->SetSockName( sSockName );
|
|
this->push_back( pcSock );
|
|
}
|
|
|
|
Csock * CSocketManager::FindSockByRemotePort( u_short iPort )
|
|
{
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
{
|
|
if(( *this )[i]->GetRemotePort() == iPort )
|
|
return(( *this )[i] );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
Csock * CSocketManager::FindSockByLocalPort( u_short iPort )
|
|
{
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
if(( *this )[i]->GetLocalPort() == iPort )
|
|
return(( *this )[i] );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
Csock * CSocketManager::FindSockByName( const CS_STRING & sName )
|
|
{
|
|
std::vector<Csock *>::iterator it;
|
|
std::vector<Csock *>::iterator it_end = this->end();
|
|
for( it = this->begin(); it != it_end; it++ )
|
|
if(( *it )->GetSockName() == sName )
|
|
return( *it );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
Csock * CSocketManager::FindSockByFD( cs_sock_t iFD )
|
|
{
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
if(( *this )[i]->GetRSock() == iFD || ( *this )[i]->GetWSock() == iFD )
|
|
return(( *this )[i] );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
std::vector<Csock *> CSocketManager::FindSocksByName( const CS_STRING & sName )
|
|
{
|
|
std::vector<Csock *> vpSocks;
|
|
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
if(( *this )[i]->GetSockName() == sName )
|
|
vpSocks.push_back(( *this )[i] );
|
|
|
|
return( vpSocks );
|
|
}
|
|
|
|
std::vector<Csock *> CSocketManager::FindSocksByRemoteHost( const CS_STRING & sHostname )
|
|
{
|
|
std::vector<Csock *> vpSocks;
|
|
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
if(( *this )[i]->GetHostName() == sHostname )
|
|
vpSocks.push_back(( *this )[i] );
|
|
|
|
return( vpSocks );
|
|
}
|
|
|
|
void CSocketManager::DelSockByAddr( Csock *pcSock )
|
|
{
|
|
for( u_int a = 0; a < this->size(); a++ )
|
|
{
|
|
if( pcSock == ( *this )[a] )
|
|
{
|
|
DelSock( a );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
void CSocketManager::DelSock( u_int iPos )
|
|
{
|
|
if( iPos >= this->size() )
|
|
{
|
|
CS_DEBUG( "Invalid Sock Position Requested! [" << iPos << "]" );
|
|
return;
|
|
}
|
|
|
|
Csock * pSock = ( *this )[iPos];
|
|
|
|
if( pSock->GetCloseType() != Csock::CLT_DEREFERENCE )
|
|
{
|
|
if( pSock->IsConnected() )
|
|
pSock->Disconnected(); // only call disconnected event if connected event was called (IE IsConnected was set)
|
|
|
|
m_iBytesRead += pSock->GetBytesRead();
|
|
m_iBytesWritten += pSock->GetBytesWritten();
|
|
}
|
|
|
|
CS_Delete( pSock );
|
|
this->erase( this->begin() + iPos );
|
|
}
|
|
|
|
bool CSocketManager::SwapSockByIdx( Csock *pNewSock, u_long iOrginalSockIdx )
|
|
{
|
|
if( iOrginalSockIdx >= this->size() )
|
|
{
|
|
CS_DEBUG( "Invalid Sock Position Requested! [" << iOrginalSockIdx << "]" );
|
|
return( false );
|
|
}
|
|
|
|
Csock *pSock = ( *this )[iOrginalSockIdx];
|
|
pNewSock->Copy( *pSock );
|
|
pSock->Dereference();
|
|
( *this )[iOrginalSockIdx] = ( Csock * )pNewSock;
|
|
this->push_back(( Csock * )pSock ); // this allows it to get cleaned up
|
|
return( true );
|
|
}
|
|
|
|
bool CSocketManager::SwapSockByAddr( Csock *pNewSock, Csock *pOrigSock )
|
|
{
|
|
for( u_long a = 0; a < this->size(); a++ )
|
|
{
|
|
if(( *this )[a] == pOrigSock )
|
|
return( SwapSockByIdx( pNewSock, a ) );
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
unsigned long long CSocketManager::GetBytesRead() const
|
|
{
|
|
// Start with the total bytes read from destroyed sockets
|
|
unsigned long long iRet = m_iBytesRead;
|
|
|
|
// Add in the outstanding bytes read from active sockets
|
|
for( u_int a = 0; a < this->size(); a++ )
|
|
iRet += ( *this )[a]->GetBytesRead();
|
|
|
|
return( iRet );
|
|
}
|
|
|
|
unsigned long long CSocketManager::GetBytesWritten() const
|
|
{
|
|
// Start with the total bytes written to destroyed sockets
|
|
unsigned long long iRet = m_iBytesWritten;
|
|
|
|
// Add in the outstanding bytes written to active sockets
|
|
for( u_int a = 0; a < this->size(); a++ )
|
|
iRet += ( *this )[a]->GetBytesWritten();
|
|
|
|
return( iRet );
|
|
}
|
|
|
|
void CSocketManager::FDSetCheck( int iFd, std::map< int, short > & miiReadyFds, ECheckType eType )
|
|
{
|
|
std::map< int, short >::iterator it = miiReadyFds.find( iFd );
|
|
if( it != miiReadyFds.end() )
|
|
it->second = ( short )( it->second | eType ); // TODO need to figure out why |= throws 'short int' from 'int' may alter its value
|
|
else
|
|
miiReadyFds[iFd] = eType;
|
|
}
|
|
bool CSocketManager::FDHasCheck( int iFd, std::map< int, short > & miiReadyFds, ECheckType eType )
|
|
{
|
|
std::map< int, short >::iterator it = miiReadyFds.find( iFd );
|
|
if( it != miiReadyFds.end() )
|
|
return(( it->second & eType ) );
|
|
return( false );
|
|
}
|
|
|
|
int CSocketManager::Select( std::map< int, short > & miiReadyFds, struct timeval *tvtimeout )
|
|
{
|
|
AssignFDs( miiReadyFds, tvtimeout );
|
|
#ifdef CSOCK_USE_POLL
|
|
if( miiReadyFds.empty() )
|
|
return( select( 0, NULL, NULL, NULL, tvtimeout ) );
|
|
|
|
struct pollfd * pFDs = ( struct pollfd * )malloc( sizeof( struct pollfd ) * miiReadyFds.size() );
|
|
size_t uCurrPoll = 0;
|
|
for( std::map< int, short >::iterator it = miiReadyFds.begin(); it != miiReadyFds.end(); ++it, ++uCurrPoll )
|
|
{
|
|
short iEvents = 0;
|
|
if( it->second & ECT_Read )
|
|
iEvents |= POLLIN;
|
|
if( it->second & ECT_Write )
|
|
iEvents |= POLLOUT;
|
|
pFDs[uCurrPoll].fd = it->first;
|
|
pFDs[uCurrPoll].events = iEvents;
|
|
pFDs[uCurrPoll].revents = 0;
|
|
}
|
|
int iTimeout = ( int )( tvtimeout->tv_usec / 1000 );
|
|
iTimeout += ( int )( tvtimeout->tv_sec * 1000 );
|
|
size_t uMaxFD = miiReadyFds.size();
|
|
int iRet = poll( pFDs, uMaxFD, iTimeout );
|
|
miiReadyFds.clear();
|
|
for( uCurrPoll = 0; uCurrPoll < uMaxFD; ++uCurrPoll )
|
|
{
|
|
short iEvents = 0;
|
|
if(( pFDs[uCurrPoll].revents & ( POLLIN|POLLERR|POLLHUP|POLLNVAL ) ) )
|
|
iEvents |= ECT_Read;
|
|
if(( pFDs[uCurrPoll].revents & POLLOUT ) )
|
|
iEvents |= ECT_Write;
|
|
std::map< int, short >::iterator it = miiReadyFds.find( pFDs[uCurrPoll].fd );
|
|
if( it != miiReadyFds.end() )
|
|
it->second = ( short )( it->second | iEvents ); // TODO need to figure out why |= throws 'short int' from 'int' may alter its value
|
|
else
|
|
miiReadyFds[pFDs[uCurrPoll].fd] = iEvents;
|
|
}
|
|
free( pFDs );
|
|
#else
|
|
fd_set rfds, wfds;
|
|
TFD_ZERO( &rfds );
|
|
TFD_ZERO( &wfds );
|
|
bool bHasWrite = false;
|
|
int iHighestFD = 0;
|
|
for( std::map< int, short >::iterator it = miiReadyFds.begin(); it != miiReadyFds.end(); ++it )
|
|
{
|
|
iHighestFD = std::max( it->first, iHighestFD );
|
|
if( it->second & ECT_Read )
|
|
{
|
|
TFD_SET( it->first, &rfds );
|
|
}
|
|
if( it->second & ECT_Write )
|
|
{
|
|
bHasWrite = true;
|
|
TFD_SET( it->first, &wfds );
|
|
}
|
|
}
|
|
|
|
int iRet = select( iHighestFD + 1, &rfds, ( bHasWrite ? &wfds : NULL ), NULL, tvtimeout );
|
|
if( iRet <= 0 )
|
|
miiReadyFds.clear();
|
|
else
|
|
{
|
|
for( std::map< int, short >::iterator it = miiReadyFds.begin(); it != miiReadyFds.end(); ++it )
|
|
{
|
|
if(( it->second & ECT_Read ) && !TFD_ISSET( it->first, &rfds ) )
|
|
it->second &= ~ECT_Read;
|
|
if(( it->second & ECT_Write ) && !TFD_ISSET( it->first, &wfds ) )
|
|
it->second &= ~ECT_Write;
|
|
}
|
|
}
|
|
#endif /* CSOCK_USE_POLL */
|
|
|
|
return( iRet );
|
|
}
|
|
|
|
void CSocketManager::Select( std::map<Csock *, EMessages> & mpeSocks )
|
|
{
|
|
mpeSocks.clear();
|
|
struct timeval tv;
|
|
|
|
std::map< int, short > miiReadyFds;
|
|
tv.tv_sec = m_iSelectWait / 1000000;
|
|
tv.tv_usec = m_iSelectWait % 1000000;
|
|
u_int iQuickReset = 1000;
|
|
if( m_iSelectWait == 0 )
|
|
iQuickReset = 0;
|
|
|
|
bool bHasAvailSocks = false;
|
|
unsigned long long iNOW = 0;
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
{
|
|
Csock *pcSock = ( *this )[i];
|
|
|
|
Csock::ECloseType eCloseType = pcSock->GetCloseType();
|
|
|
|
if( eCloseType == Csock::CLT_NOW || eCloseType == Csock::CLT_DEREFERENCE || ( eCloseType == Csock::CLT_AFTERWRITE && pcSock->GetWriteBuffer().empty() ) )
|
|
{
|
|
DelSock( i-- ); // close any socks that have requested it
|
|
continue;
|
|
}
|
|
else
|
|
pcSock->Cron(); // call the Cron handler here
|
|
|
|
cs_sock_t & iRSock = pcSock->GetRSock();
|
|
cs_sock_t & iWSock = pcSock->GetWSock();
|
|
#if !defined(CSOCK_USE_POLL) && !defined(_WIN32)
|
|
if( iRSock > FD_SETSIZE || iWSock > FD_SETSIZE )
|
|
{
|
|
CS_DEBUG( "FD is larger than select() can handle" );
|
|
DelSock( i-- );
|
|
continue;
|
|
}
|
|
#endif /* CSOCK_USE_POLL */
|
|
|
|
#ifdef HAVE_C_ARES
|
|
ares_channel pChannel = pcSock->GetAresChannel();
|
|
if( pChannel )
|
|
{
|
|
ares_socket_t aiAresSocks[1];
|
|
aiAresSocks[0] = ARES_SOCKET_BAD;
|
|
int iSockMask = ares_getsock( pChannel, aiAresSocks, 1 );
|
|
if( ARES_GETSOCK_READABLE( iSockMask, 0 ) )
|
|
FDSetCheck( aiAresSocks[0], miiReadyFds, ECT_Read );
|
|
if( ARES_GETSOCK_WRITABLE( iSockMask, 0 ) )
|
|
FDSetCheck( aiAresSocks[0], miiReadyFds, ECT_Write );
|
|
// let ares drop the timeout if it has something timing out sooner then whats in tv currently
|
|
ares_timeout( pChannel, &tv, &tv );
|
|
}
|
|
#endif /* HAVE_C_ARES */
|
|
if( pcSock->GetType() == Csock::LISTENER && pcSock->GetConState() == Csock::CST_BINDVHOST )
|
|
{
|
|
if( !pcSock->Listen( pcSock->GetPort(), pcSock->GetMaxConns(), pcSock->GetBindHost(), pcSock->GetTimeout(), true ) )
|
|
{
|
|
pcSock->Close();
|
|
DelSock( i-- );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
pcSock->AssignFDs( miiReadyFds, &tv );
|
|
|
|
if( pcSock->GetConState() != Csock::CST_OK )
|
|
continue;
|
|
|
|
bHasAvailSocks = true;
|
|
|
|
bool bIsReadPaused = pcSock->IsReadPaused();
|
|
if( bIsReadPaused )
|
|
{
|
|
pcSock->ReadPaused();
|
|
bIsReadPaused = pcSock->IsReadPaused(); // re-read it again, incase it changed status)
|
|
}
|
|
if( iRSock == CS_INVALID_SOCK || iWSock == CS_INVALID_SOCK )
|
|
{
|
|
SelectSock( mpeSocks, SUCCESS, pcSock );
|
|
continue; // invalid sock fd
|
|
}
|
|
|
|
if( pcSock->GetType() != Csock::LISTENER )
|
|
{
|
|
bool bHasWriteBuffer = !pcSock->GetWriteBuffer().empty();
|
|
|
|
if( !bIsReadPaused )
|
|
FDSetCheck( iRSock, miiReadyFds, ECT_Read );
|
|
|
|
if( pcSock->AllowWrite( iNOW ) && ( !pcSock->IsConnected() || bHasWriteBuffer ) )
|
|
{
|
|
if( !pcSock->IsConnected() )
|
|
{
|
|
// set the write bit if not connected yet
|
|
FDSetCheck( iWSock, miiReadyFds, ECT_Write );
|
|
}
|
|
else if( bHasWriteBuffer && !pcSock->GetSSL() )
|
|
{
|
|
// always set the write bit if there is data to send when NOT ssl
|
|
FDSetCheck( iWSock, miiReadyFds, ECT_Write );
|
|
}
|
|
else if( bHasWriteBuffer && pcSock->GetSSL() && pcSock->SslIsEstablished() )
|
|
{
|
|
// ONLY set the write bit if there is data to send and the SSL handshake is finished
|
|
FDSetCheck( iWSock, miiReadyFds, ECT_Write );
|
|
}
|
|
}
|
|
|
|
if( pcSock->GetSSL() && !pcSock->SslIsEstablished() && bHasWriteBuffer )
|
|
{
|
|
// if this is an unestabled SSL session with data to send ... try sending it
|
|
// do this here, cause otherwise ssl will cause a small
|
|
// cpu spike waiting for the handshake to finish
|
|
// resend this data
|
|
if( !pcSock->Write( "" ) )
|
|
{
|
|
pcSock->Close();
|
|
}
|
|
// warning ... setting write bit in here causes massive CPU spinning on invalid SSL servers
|
|
// http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=631590
|
|
// however, we can set the select WAY down and it will retry quickly, but keep it from spinning at 100%
|
|
tv.tv_usec = iQuickReset;
|
|
tv.tv_sec = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FDSetCheck( iRSock, miiReadyFds, ECT_Read );
|
|
}
|
|
|
|
if( pcSock->GetSSL() && pcSock->GetType() != Csock::LISTENER )
|
|
{
|
|
if(( pcSock->GetPending() > 0 ) && ( !pcSock->IsReadPaused() ) )
|
|
SelectSock( mpeSocks, SUCCESS, pcSock );
|
|
}
|
|
}
|
|
|
|
// old fashion select, go fer it
|
|
int iSel;
|
|
|
|
if( !mpeSocks.empty() ) // .1 ms pause to see if anything else is ready (IE if there is SSL data pending, don't wait too long)
|
|
{
|
|
tv.tv_usec = iQuickReset;
|
|
tv.tv_sec = 0;
|
|
}
|
|
else if( !this->empty() && !bHasAvailSocks )
|
|
{
|
|
tv.tv_usec = iQuickReset;
|
|
tv.tv_sec = 0;
|
|
}
|
|
|
|
iSel = Select( miiReadyFds, &tv );
|
|
|
|
if( iSel == 0 )
|
|
{
|
|
if( mpeSocks.empty() )
|
|
m_errno = SELECT_TIMEOUT;
|
|
else
|
|
m_errno = SUCCESS;
|
|
#ifdef HAVE_C_ARES
|
|
// run through ares channels and process timeouts
|
|
for( u_long uSock = 0; uSock < this->size(); ++uSock )
|
|
{
|
|
Csock *pcSock = this->at( uSock );
|
|
ares_channel pChannel = pcSock->GetAresChannel();
|
|
if( pChannel )
|
|
ares_process_fd( pChannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD );
|
|
}
|
|
#endif /* HAVE_C_ARES */
|
|
|
|
return;
|
|
}
|
|
|
|
if(( iSel == -1 ) && ( errno == EINTR ) )
|
|
{
|
|
if( mpeSocks.empty() )
|
|
m_errno = SELECT_TRYAGAIN;
|
|
else
|
|
m_errno = SUCCESS;
|
|
|
|
return;
|
|
}
|
|
else if( iSel == -1 )
|
|
{
|
|
if( mpeSocks.empty() )
|
|
m_errno = SELECT_ERROR;
|
|
else
|
|
m_errno = SUCCESS;
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
m_errno = SUCCESS;
|
|
}
|
|
|
|
CheckFDs( miiReadyFds );
|
|
|
|
// find out wich one is ready
|
|
for( unsigned int i = 0; i < this->size(); i++ )
|
|
{
|
|
Csock *pcSock = ( *this )[i];
|
|
|
|
#ifdef HAVE_C_ARES
|
|
ares_channel pChannel = pcSock->GetAresChannel();
|
|
if( pChannel )
|
|
{
|
|
ares_socket_t aiAresSocks[1];
|
|
aiAresSocks[0] = ARES_SOCKET_BAD;
|
|
ares_getsock( pChannel, aiAresSocks, 1 );
|
|
if( FDHasCheck( aiAresSocks[0], miiReadyFds, ECT_Read ) || FDHasCheck( aiAresSocks[0], miiReadyFds, ECT_Write ) )
|
|
ares_process_fd( pChannel, aiAresSocks[0], aiAresSocks[0] );
|
|
}
|
|
#endif /* HAVE_C_ARES */
|
|
pcSock->CheckFDs( miiReadyFds );
|
|
|
|
if( pcSock->GetConState() != Csock::CST_OK )
|
|
continue;
|
|
|
|
cs_sock_t & iRSock = pcSock->GetRSock();
|
|
cs_sock_t & iWSock = pcSock->GetWSock();
|
|
EMessages iErrno = SUCCESS;
|
|
|
|
if( iRSock == CS_INVALID_SOCK || iWSock == CS_INVALID_SOCK )
|
|
{
|
|
// trigger a success so it goes through the normal motions
|
|
// and an error is produced
|
|
SelectSock( mpeSocks, SUCCESS, pcSock );
|
|
continue; // watch for invalid socks
|
|
}
|
|
|
|
if( FDHasCheck( iWSock, miiReadyFds, ECT_Write ) )
|
|
{
|
|
if( iSel > 0 )
|
|
{
|
|
iErrno = SUCCESS;
|
|
if(( !pcSock->GetWriteBuffer().empty() ) && ( pcSock->IsConnected() ) )
|
|
{
|
|
// write whats in the socks send buffer
|
|
if( !pcSock->Write( "" ) )
|
|
{
|
|
// write failed, sock died :(
|
|
iErrno = SELECT_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
iErrno = SELECT_ERROR;
|
|
|
|
SelectSock( mpeSocks, iErrno, pcSock );
|
|
|
|
}
|
|
else if( FDHasCheck( iRSock, miiReadyFds, ECT_Read ) )
|
|
{
|
|
if( iSel > 0 )
|
|
iErrno = SUCCESS;
|
|
else
|
|
iErrno = SELECT_ERROR;
|
|
|
|
if( pcSock->GetType() != Csock::LISTENER )
|
|
SelectSock( mpeSocks, iErrno, pcSock );
|
|
else // someone is coming in!
|
|
{
|
|
CS_STRING sHost;
|
|
u_short port;
|
|
cs_sock_t inSock = pcSock->Accept( sHost, port );
|
|
|
|
if( inSock != CS_INVALID_SOCK )
|
|
{
|
|
if( Csock::TMO_ACCEPT & pcSock->GetTimeoutType() )
|
|
pcSock->ResetTimer(); // let them now it got dinged
|
|
|
|
// if we have a new sock, then add it
|
|
Csock *NewpcSock = ( Csock * )pcSock->GetSockObj( sHost, port );
|
|
|
|
if( !NewpcSock )
|
|
NewpcSock = GetSockObj( sHost, port );
|
|
|
|
NewpcSock->SetType( Csock::INBOUND );
|
|
NewpcSock->SetRSock( inSock );
|
|
NewpcSock->SetWSock( inSock );
|
|
NewpcSock->SetIPv6( pcSock->GetIPv6() );
|
|
|
|
bool bAddSock = true;
|
|
#ifdef HAVE_LIBSSL
|
|
//
|
|
// is this ssl ?
|
|
if( pcSock->GetSSL() )
|
|
{
|
|
NewpcSock->SetCipher( pcSock->GetCipher() );
|
|
NewpcSock->SetPemLocation( pcSock->GetPemLocation() );
|
|
NewpcSock->SetPemPass( pcSock->GetPemPass() );
|
|
NewpcSock->SetRequireClientCertFlags( pcSock->GetRequireClientCertFlags() );
|
|
bAddSock = NewpcSock->AcceptSSL();
|
|
}
|
|
|
|
#endif /* HAVE_LIBSSL */
|
|
if( bAddSock )
|
|
{
|
|
// set the name of the listener
|
|
NewpcSock->SetParentSockName( pcSock->GetSockName() );
|
|
NewpcSock->SetRate( pcSock->GetRateBytes(), pcSock->GetRateTime() );
|
|
if( NewpcSock->GetSockName().empty() )
|
|
{
|
|
std::stringstream s;
|
|
s << sHost << ":" << port;
|
|
AddSock( NewpcSock, s.str() );
|
|
|
|
}
|
|
else
|
|
AddSock( NewpcSock, NewpcSock->GetSockName() );
|
|
}
|
|
else
|
|
CS_Delete( NewpcSock );
|
|
}
|
|
#ifdef _WIN32
|
|
else if( GetSockError() != WSAEWOULDBLOCK )
|
|
#else /* _WIN32 */
|
|
else if( GetSockError() != EAGAIN )
|
|
#endif /* _WIN32 */
|
|
{
|
|
pcSock->CallSockError( GetSockError() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void MinimizeTime( timeval& min, const timeval& another )
|
|
{
|
|
if( timercmp( &min, &another, > ) )
|
|
{
|
|
min = another;
|
|
}
|
|
}
|
|
|
|
timeval CSocketManager::GetDynamicSleepTime( const timeval& tNow, const timeval& tMaxResolution ) const
|
|
{
|
|
timeval tNextRunTime;
|
|
timeradd( &tNow, &tMaxResolution, &tNextRunTime );
|
|
std::vector<Csock *>::const_iterator it;
|
|
// This is safe, because we don't modify the vector.
|
|
std::vector<Csock *>::const_iterator it_end = this->end();
|
|
|
|
for( it = this->begin(); it != it_end; it++ )
|
|
{
|
|
Csock* pSock = *it;
|
|
|
|
if( pSock->GetConState() != Csock::CST_OK )
|
|
tNextRunTime = tNow; // this is in a nebulous state, need to let it proceed like normal
|
|
|
|
time_t iTimeoutInSeconds = pSock->GetTimeout();
|
|
if( iTimeoutInSeconds > 0 )
|
|
{
|
|
timeval tNextTimeout;
|
|
tNextTimeout.tv_sec = pSock->GetNextCheckTimeout( 0 ); // TODO convert socket timeouts to timeval too?
|
|
tNextTimeout.tv_usec = 0;
|
|
MinimizeTime( tNextRunTime, tNextTimeout );
|
|
}
|
|
|
|
const std::vector<CCron *> & vCrons = pSock->GetCrons();
|
|
std::vector<CCron *>::const_iterator cit;
|
|
std::vector<CCron *>::const_iterator cit_end = vCrons.end();
|
|
for( cit = vCrons.begin(); cit != cit_end; cit++ )
|
|
MinimizeTime( tNextRunTime, ( *cit )->GetNextRun() );
|
|
}
|
|
std::vector<CCron *>::const_iterator cit;
|
|
std::vector<CCron *>::const_iterator cit_end = m_vcCrons.end();
|
|
for( cit = m_vcCrons.begin(); cit != cit_end; cit++ )
|
|
MinimizeTime( tNextRunTime, ( *cit )->GetNextRun() );
|
|
|
|
timeval tReturnValue;
|
|
if( timercmp( &tNextRunTime, &tNow, < ) )
|
|
{
|
|
timerclear( &tReturnValue );
|
|
return( tReturnValue ); // smallest unit possible
|
|
}
|
|
timersub( &tNextRunTime, &tNow, &tReturnValue );
|
|
MinimizeTime( tReturnValue, tMaxResolution );
|
|
return( tReturnValue );
|
|
}
|
|
|
|
void CSocketManager::SelectSock( std::map<Csock *, EMessages> & mpeSocks, EMessages eErrno, Csock * pcSock )
|
|
{
|
|
if( mpeSocks.find( pcSock ) != mpeSocks.end() )
|
|
return;
|
|
|
|
mpeSocks[pcSock] = eErrno;
|
|
}
|
|
|