From 493d515490bdc4d9327bf9d70fdc194943df78e4 Mon Sep 17 00:00:00 2001 From: imaginos Date: Tue, 1 Apr 2008 00:22:43 +0000 Subject: [PATCH] this should fix all the ipv4/ipv6 issues, tested it on fbsd and linux git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@999 726aef4b-f618-498e-8847-2d620e286838 --- Csocket.cpp | 331 ++++++++++++---------------------------------------- Csocket.h | 113 ++++-------------- 2 files changed, 100 insertions(+), 344 deletions(-) diff --git a/Csocket.cpp b/Csocket.cpp index 53144471..4089be6f 100644 --- a/Csocket.cpp +++ b/Csocket.cpp @@ -28,7 +28,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * -* $Revision: 1.73 $ +* $Revision: 1.78 $ */ #include "Csocket.h" @@ -36,6 +36,8 @@ #include #endif /* __NetBSD__ */ +#include + #define CS_SRANDBUFFER 128 using namespace std; @@ -172,6 +174,7 @@ static int __GetHostByName( const CS_STRING & sHostName, struct in_addr *paddr, int GetAddrInfo( const CS_STRING & sHostname, Csock *pSock, CSSockAddr & csSockAddr ) { #ifndef HAVE_IPV6 + // if ipv6 is not enabled, then simply use gethostbyname, nothing special outside of this is done if( pSock ) pSock->SetIPv6( false ); csSockAddr.SetIPv6( false ); @@ -186,23 +189,25 @@ int GetAddrInfo( const CS_STRING & sHostname, Csock *pSock, CSSockAddr & csSockA hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - #ifdef AI_ADDRCONFIG - hints.ai_flags = AI_ADDRCONFIG; + // this is suppose to eliminate host from appearing that this system can not support + hints.ai_flags = AI_ADDRCONFIG; #endif /* AI_ADDRCONFIG */ + if( pSock && pSock->GetType() == Csock::LISTENER || pSock->GetConState() == Csock::CST_BINDVHOST ) + { // when doing a dns for bind only, set the AI_PASSIVE flag as suggested by the man page + hints.ai_flags |= AI_PASSIVE; + } int iRet = getaddrinfo( sHostname.c_str(), NULL, &hints, &res ); if( iRet == EAI_AGAIN ) - return( EAGAIN ); + return( EAGAIN ); // need to return telling the user to try again else if( ( iRet == 0 ) && ( res ) ) - { - struct addrinfo *pUseAddr = NULL; -#ifdef __RANDOMIZE_SOURCE_ADDRESSES - std::vector< struct addrinfo *> vHostCanidates; -#endif /* __RANDOMIZE_SOURCE_ADDRESSES */ + { + std::list lpTryAddrs; + bool bFound = false; for( struct addrinfo *pRes = res; 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 @@ -212,67 +217,66 @@ int GetAddrInfo( const CS_STRING & sHostname, Csock *pSock, CSSockAddr & csSockA if( ( csSockAddr.GetAFRequire() != CSSockAddr::RAF_ANY ) && ( pRes->ai_family != csSockAddr.GetAFRequire() ) ) continue; // they requested a special type, so be certain we woop past anything unwanted - + lpTryAddrs.push_back( pRes ); + } + for( std::list::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 ) { -#ifdef __RANDOMIZE_SOURCE_ADDRESSES - vHostCanidates.push_back( pRes ); -#else - pUseAddr = pRes; -#endif /* __RANDOMIZE_SOURCE_ADDRESSES */ + if( pSock ) + pSock->SetIPv6( false ); + csSockAddr.SetIPv6( false ); + struct sockaddr_in *pTmp = (struct sockaddr_in *)pRes->ai_addr; + memcpy( csSockAddr.GetAddr(), &(pTmp->sin_addr), sizeof( *(csSockAddr.GetAddr()) ) ); + if( pSock && pSock->GetConState() == Csock::CST_DESTDNS && pSock->GetType() == Csock::OUTBOUND ) + { + bTryConnect = true; + } + else + { + bFound = true; + break; + } } else if( pRes->ai_family == AF_INET6 ) { -#ifdef __RANDOMIZE_SOURCE_ADDRESSES - vHostCanidates.push_back( pRes ); -#else - pUseAddr = pRes; -#endif /* __RANDOMIZE_SOURCE_ADDRESSES */ + if( pSock ) + pSock->SetIPv6( true ); + csSockAddr.SetIPv6( true ); + struct sockaddr_in6 *pTmp = (struct sockaddr_in6 *)pRes->ai_addr; + memcpy( csSockAddr.GetAddr6(), &(pTmp->sin6_addr), sizeof( *(csSockAddr.GetAddr6()) ) ); + if( pSock && pSock->GetConState() == Csock::CST_DESTDNS && pSock->GetType() == Csock::OUTBOUND ) + { + bTryConnect = true; + } + else + { + bFound = true; + break; + } } - } -#ifdef __RANDOMIZE_SOURCE_ADDRESSES - if( vHostCanidates.size() > 1 ) - { - // this is basically where getaddrinfo() sorts the list of results. basically to help out what round robin dns does, - // pick a random canidate to make this work - struct random_data cRandData; - char chState[CS_SRANDBUFFER]; - int32_t iNumber = 0; - if( initstate_r( (u_int)millitime(), chState, CS_SRANDBUFFER, &cRandData ) == 0 && random_r( &cRandData, &iNumber ) == 0 ) - { - iNumber %= (int)vHostCanidates.size(); - pUseAddr = vHostCanidates[iNumber]; - } - else - { - CS_DEBUG( "initstate_r/random_r failed" ); - pUseAddr = vHostCanidates[0]; - } - } - else if( vHostCanidates.size() ) - { - pUseAddr = vHostCanidates[0]; - } -#endif /* __RANDOMIZE_SOURCE_ADDRESSES */ - if( pUseAddr && pUseAddr->ai_family == AF_INET ) - { - if( pSock ) - pSock->SetIPv6( false ); - csSockAddr.SetIPv6( false ); - struct sockaddr_in *pTmp = (struct sockaddr_in *)pUseAddr->ai_addr; - memcpy( csSockAddr.GetAddr(), &(pTmp->sin_addr), sizeof( *(csSockAddr.GetAddr()) ) ); - } - else if( pUseAddr ) - { - if( pSock ) - pSock->SetIPv6( true ); - csSockAddr.SetIPv6( true ); - struct sockaddr_in6 *pTmp = (struct sockaddr_in6 *)pUseAddr->ai_addr; - memcpy( csSockAddr.GetAddr6(), &(pTmp->sin6_addr), sizeof( *(csSockAddr.GetAddr6()) ) ); + 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( pSock->CreateSocksFD() && pSock->Connect( pSock->GetBindHost(), true ) ) + { + pSock->SetSkipConnect( true ); // this tells the socket that the connection state has been started + bFound = true; + break; + } + } + else if( bTryConnect ) + { + bFound = true; + } } + freeaddrinfo( res ); - if( pUseAddr ) // the data pointed to here is invalid now, but the pointer itself is a good test + if( bFound ) // the data pointed to here is invalid now, but the pointer itself is a good test { return( 0 ); } @@ -281,110 +285,6 @@ int GetAddrInfo( const CS_STRING & sHostname, Csock *pSock, CSSockAddr & csSockA return( ETIMEDOUT ); } -#ifdef ___DO_THREADS -CSMutex::CSMutex() -{ - pthread_mutexattr_init( &m_mattrib ); - if ( pthread_mutexattr_settype( &m_mattrib, PTHREAD_MUTEX_FAST_NP ) != 0 ) - throw CS_STRING( "ERROR: pthread_mutexattr_settype failed!" ); - - if ( pthread_mutex_init( &m_mutex, &m_mattrib ) != 0 ) - throw CS_STRING( "ERROR: pthread_mutex_init failed!" ); -} - -CSMutex::~CSMutex() -{ - pthread_mutexattr_destroy( &m_mattrib ); - pthread_mutex_destroy( &m_mutex ); -} - -bool CSThread::start() -{ - // mark the job as running - lock(); - m_eStatus = RUNNING; - unlock(); - - pthread_attr_t attr; - if ( pthread_attr_init( &attr ) != 0 ) - { - WARN( "pthread_attr_init failed" ); - lock(); - m_eStatus = FINISHED; - unlock(); - return( false ); - } - - if ( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ) != 0 ) - { - WARN( "pthread_attr_setdetachstate failed" ); - lock(); - m_eStatus = FINISHED; - unlock(); - return( false ); - } - - int iRet = pthread_create( &m_ppth, &attr, start_thread, this ); - if ( iRet != 0 ) - { - WARN( "pthread_create failed " ); - lock(); - m_eStatus = FINISHED; - unlock(); - return( false ); - } - - return( true ); -} - -void CSThread::wait() -{ - while( true ) - { - lock(); - EStatus e = Status(); - unlock(); - if ( e == FINISHED ) - break; - usleep( 100 ); - } -} - -void *CSThread::start_thread( void *args ) -{ - CSThread *curThread = (CSThread *)args; - curThread->run(); - curThread->lock(); - curThread->SetStatus( CSThread::FINISHED ); - curThread->unlock(); - pthread_exit( NULL ); -} - -void CDNSResolver::Lookup( const CS_STRING & sHostname ) -{ - m_bSuccess = false; - m_sHostname = sHostname; - start(); -} - -void CDNSResolver::run() -{ - m_bSuccess = false; - if( GetAddrInfo( m_sHostname, NULL, m_cSockAddr ) == 0 ) - m_bSuccess = true; -} - -bool CDNSResolver::IsCompleted() -{ - lock(); - EStatus e = Status(); - unlock(); - if ( e == FINISHED ) - return( true ); - return( false ); -} - -#endif /* ___DO_THREADS */ #ifdef HAVE_LIBSSL bool InitSSL( ECompType eCompressionType ) { @@ -552,9 +452,6 @@ Csock::Csock( int itimeout ) #ifdef HAVE_LIBSSL m_pCerVerifyCB = NULL; #endif /* HAVE_LIBSSL */ -#ifdef ___DO_THREADS - m_pResolver = NULL; -#endif /* ___DO_THREADS */ Init( "", 0, itimeout ); } @@ -563,9 +460,6 @@ Csock::Csock( const CS_STRING & sHostname, u_short iport, int itimeout ) #ifdef HAVE_LIBSSL m_pCerVerifyCB = NULL; #endif /* HAVE_LIBSSL */ -#ifdef ___DO_THREADS - m_pResolver = NULL; -#endif /* ___DO_THREADS */ Init( sHostname, iport, itimeout ); } @@ -583,18 +477,6 @@ Csock *Csock::GetSockObj( const CS_STRING & sHostname, u_short iPort ) Csock::~Csock() { -#ifdef ___DO_THREADS - if( m_pResolver ) - { - m_pResolver->lock(); - CDNSResolver::EStatus eStatus = m_pResolver->Status(); - m_pResolver->unlock(); - if ( eStatus == CDNSResolver::RUNNING ) - m_pResolver->cancel(); - Zzap( m_pResolver ); - } -#endif /* __DO_THREADS_ */ - #ifdef HAVE_LIBSSL FREE_SSL(); FREE_CTX(); @@ -619,9 +501,6 @@ Csock::~Csock() void Csock::Dereference() { -#ifdef ___DO_THREADS - m_pResolver = NULL; -#endif /* __DO_THREADS_ */ m_iWriteSock = m_iReadSock = -1; #ifdef HAVE_LIBSSL @@ -677,6 +556,7 @@ void Csock::Copy( const Csock & cCopy ) m_address = cCopy.m_address; m_bindhost = cCopy.m_bindhost; m_bIsIPv6 = cCopy.m_bIsIPv6; + m_bSkipConnect = cCopy.m_bSkipConnect; #ifdef HAVE_LIBSSL m_sSSLBuffer = cCopy.m_sSSLBuffer; @@ -706,11 +586,6 @@ void Csock::Copy( const Csock & cCopy ) m_iCurBindCount = cCopy.m_iCurBindCount; m_iDNSTryCount = cCopy.m_iDNSTryCount; -#ifdef ___DO_THREADS - if( m_pResolver ) - CS_Delete( m_pResolver ); - m_pResolver = cCopy.m_pResolver; -#endif /* ___DO_THREADS */ } Csock & Csock::operator<<( const CS_STRING & s ) @@ -777,6 +652,10 @@ Csock & Csock::operator<<( double i ) bool Csock::Connect( const CS_STRING & sBindHost, bool bSkipSetup ) { + if( m_bSkipConnect ) + { // this was already called, so skipping now. this is to allow easy pass through + return( true ); + } // bind to a hostname if requested m_sBindHost = sBindHost; if ( !bSkipSetup ) @@ -2050,7 +1929,7 @@ int Csock::DNSLookup( EDNSLType eDNSLType ) if ( m_sBindHost.empty() ) { if ( m_eConState != CST_OK ) - m_eConState = CST_VHOSTDNS; // skip binding, there is no vhost + m_eConState = CST_DESTDNS; // skip binding, there is no vhost return( 0 ); } @@ -2058,66 +1937,6 @@ int Csock::DNSLookup( EDNSLType eDNSLType ) m_bindhost.SinPort( 0 ); } -#ifdef ___DO_THREADS - if( !m_pResolver ) - m_pResolver = new CDNSResolver; - - if ( m_iDNSTryCount == 0 ) - { - m_pResolverLookup( ( eDNSLType == DNS_VHOST ) ? m_sBindHost : m_shostname ); - m_iDNSTryCount++; - } - - if ( m_pResolverIsCompleted() ) - { - m_iDNSTryCount = 0; - if ( m_pResolverSuceeded() ) - { - if ( eDNSLType == DNS_VHOST ) - { - if( !m_pResolverGetSockAddr()->GetIPv6() ) - { - SetIPv6( false ); - memcpy( m_bindhost.GetAddr(), m_pResolverGetSockAddr()->GetAddr(), sizeof( *(m_bindhost.GetAddr()) ) ); - } -#ifdef HAVE_IPV6 - else - { - SetIPv6( true ); - memcpy( m_bindhost.GetAddr6(), m_pResolverGetSockAddr()->GetAddr6(), sizeof( *(m_bindhost.GetAddr6()) ) ); - } -#endif /* HAVE_IPV6 */ - } - else - { - if( m_pResolverGetSockAddr()->GetIPv6() ) - { - SetIPv6( false ); - memcpy( m_address.GetAddr(), m_pResolverGetSockAddr()->GetAddr(), sizeof( *(m_address.GetAddr()) ) ); - } -#ifdef HAVE_IPV6 - else - { - SetIPv6( true ); - memcpy( m_address.GetAddr6(), m_pResolverGetSockAddr()->GetAddr6(), sizeof( *(m_address.GetAddr6()) ) ); - } -#endif /* HAVE_IPV6 */ - } - - if ( m_eConState != CST_OK ) - m_eConState = ( ( eDNSLType == DNS_VHOST ) ? CST_BINDVHOST : CST_CONNECT ); // next step after vhost is to bind - - if( !CreateSocksFD() ) - return( ETIMEDOUT ); - - return( 0 ); - } - - return( ETIMEDOUT ); - } - return( EAGAIN ); - -#else int iRet = ETIMEDOUT; if ( eDNSLType == DNS_VHOST ) { @@ -2160,7 +1979,6 @@ int Csock::DNSLookup( EDNSLType eDNSLType ) } m_iDNSTryCount = 0; return( ETIMEDOUT ); -#endif /* ___DO_THREADS */ } bool Csock::SetupVHost() @@ -2168,7 +1986,7 @@ bool Csock::SetupVHost() if ( m_sBindHost.empty() ) { if ( m_eConState != CST_OK ) - m_eConState = CST_VHOSTDNS; + m_eConState = CST_DESTDNS; return( true ); } int iRet = -1; @@ -2182,7 +2000,7 @@ bool Csock::SetupVHost() if ( iRet == 0 ) { if ( m_eConState != CST_OK ) - m_eConState = CST_VHOSTDNS; + m_eConState = CST_DESTDNS; return( true ); } m_iCurBindCount++; @@ -2280,6 +2098,7 @@ void Csock::Init( const CS_STRING & sHostname, u_short iport, int itimeout ) m_iDNSTryCount = 0; m_iCurBindCount = 0; m_bIsIPv6 = false; + m_bSkipConnect = false; m_iLastCheckTimeoutTime = 0; } diff --git a/Csocket.h b/Csocket.h index 33f2e078..b733419e 100644 --- a/Csocket.h +++ b/Csocket.h @@ -28,7 +28,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * -* $Revision: 1.184 $ +* $Revision: 1.189 $ */ // note to compile with win32 need to link to winsock2, using gcc its -lws2_32 @@ -83,7 +83,7 @@ #include #include -#include "main.h" +#include "main.h" // require this as a general rule, most projects have a defines.h or the like #ifndef CS_STRING # ifdef _HAS_CSTRING_ @@ -219,6 +219,24 @@ private: }; class Csock; + +/** + * @brief this function is a wrapper around gethostbyname and getaddrinfo (for ipv6) + * + * in the event this code is using ipv6, it calls getaddrinfo, and it tries to start the connection on each iteration + * in the linked list returned by getaddrinfo. if pSock is not NULL the following behavior happens. + * - if pSock is a listener, or if the connect state is in a bind vhost state (to be used with bind) AI_PASSIVE is sent to getaddrinfo + * - if pSock is an outbound connection, AI_ADDRCONFIG and the connection is started from within this function. + * getaddrinfo might return multiple (possibly invalid depending on system configuration) ip addresses, so connect needs to try them all. + * A classic example of this is a hostname that resolves to both ipv4 and ipv6 ip's. You still need to call Connect (and ConnectSSL) to finish + * up the connection state + * - NOTE ... Once threading is reimplemented, this function will spin off a thread to resolve and return EAGAIN until its done. + * + * @param sHostname the host to resolve + * @param pSock the sock being setup, this option can be NULL, if it is null csSockAddr is only setup + * @param csSockAddr the struct that sockaddr data is being copied to + * @return 0 on success, otherwise an error. EAGAIN if this needs to be called again at a later time, ETIMEDOUT if no host is found + */ int GetAddrInfo( const CS_STRING & sHostname, Csock *pSock, CSSockAddr & csSockAddr ); //! used to retrieve the context position of the socket to its associated ssl connection. Setup once in InitSSL() via SSL_get_ex_new_index @@ -230,83 +248,6 @@ Csock *GetCsockFromCTX( X509_STORE_CTX *pCTX ); #endif /* HAVE_LIBSSL */ -#if defined( _REENTRANT ) && defined( _USE_THREADED_DNS ) -#define ___DO_THREADS -#include - -#ifndef PTHREAD_MUTEX_FAST_NP -#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL -#endif /* PTHREAD_MUTEX_FAST_NP */ - -class CSMutex -{ -public: - CSMutex (); - virtual ~CSMutex(); - int lock() { return( pthread_mutex_lock( &m_mutex ) ); } - int unlock() { return( pthread_mutex_unlock( &m_mutex ) ); } - int trylock() { return( pthread_mutex_trylock( &m_mutex ) ); } -private: - pthread_mutex_t m_mutex; - pthread_mutexattr_t m_mattrib; -}; - -class CSThread -{ -public: - CSThread() { m_eStatus = WAITING; } - virtual ~CSThread() {} - - enum EStatus - { - WAITING = 1, - RUNNING = 2, - FINISHED = 3 - }; - - bool start(); - void wait(); - virtual void run() = 0; - static void *start_thread( void *args ); - EStatus Status() { return( m_eStatus ); } - void SetStatus( EStatus e ) { m_eStatus = e; } - int lock() { return( m_mutex.lock() ); } - int unlock() { return( m_mutex.unlock() ); } - int cancel() { return( pthread_cancel( m_ppth ) ); } - -private: - pthread_t m_ppth; - EStatus m_eStatus; - CSMutex m_mutex; -}; - -class CDNSResolver : public CSThread -{ -public: - CDNSResolver() : CSThread() { m_bSuccess = false; } - virtual ~CDNSResolver() {} - //! returns imediatly, from here out check if IsCompleted() returns true before looking at ANY of the data - void Lookup( const CS_STRING & sHostname ); - - virtual void run(); - - //! true if dns entry was successfuly found - bool Suceeded() const { return( m_bSuccess ); } - - //! true if task is finished, this function is thread safe - bool IsCompleted(); - - CSSockAddr * GetSockAddr() { return( &m_cSockAddr ); } - -private: - bool m_bSuccess; - CS_STRING m_sHostname; - CSSockAddr m_cSockAddr; -}; - - -#endif /* ___DO_THREADS */ - const u_int CS_BLOCKSIZE = 4096; template inline void CS_Delete( T * & p ) { if( p ) { delete p; p = NULL; } } @@ -521,7 +462,7 @@ public: CST_START = 0, CST_DNS = CST_START, CST_BINDVHOST = 1, - CST_VHOSTDNS = 2, + CST_DESTDNS = 2, CST_CONNECT = 3, CST_OK = 4 }; @@ -1002,6 +943,8 @@ public: //! returns a const reference to the crons associated to this socket const std::vector & GetCrons() const { return( m_vcCrons ); } + void SetSkipConnect( bool b ) { m_bSkipConnect = b; } + private: //! making private for safety Csock( const Csock & cCopy ) {} @@ -1019,7 +962,7 @@ private: unsigned int m_iMaxBytes, m_iLastSend, m_iMaxStoredBufferLength, m_iTimeoutType; CSSockAddr m_address, m_bindhost; - bool m_bIsIPv6; + bool m_bIsIPv6, m_bSkipConnect; time_t m_iLastCheckTimeoutTime; #ifdef HAVE_LIBSSL @@ -1047,10 +990,6 @@ private: CS_STRING m_sBindHost; u_int m_iCurBindCount, m_iDNSTryCount; -#ifdef ___DO_THREADS - CDNSResolver m_pResolver; -#endif /* ___DO_THREADS */ - }; /** @@ -1428,7 +1367,7 @@ public: } } - if ( pcSock->GetConState() == T::CST_VHOSTDNS ) + if ( pcSock->GetConState() == T::CST_DESTDNS ) { if ( pcSock->DNSLookup( T::DNS_DEST ) == ETIMEDOUT ) { @@ -1438,7 +1377,6 @@ public: } } - if ( pcSock->GetConState() == T::CST_CONNECT ) { if ( !pcSock->Connect( pcSock->GetBindHost(), true ) ) @@ -1451,7 +1389,6 @@ public: DelSock( a-- ); continue; } - #ifdef HAVE_LIBSSL if ( pcSock->GetSSL() ) {