///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // File : kvi_ircconnectiontargetresolver.cpp // Creation date : Fri May 11 23:24:18 2002 GMT by Szymon Stefanek // // This file is part of the KVirc irc client distribution // Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net) // // This program is FREE software. You can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your opinion) any later version. // // This program is distributed in the HOPE that it will be USEFUL, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, write to the Free Software Foundation, // Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define __KVIRC__ #include "kvi_ircconnectiontargetresolver.h" #include "kvi_dns.h" #include "kvi_locale.h" #include "kvi_ircserverdb.h" #include "kvi_proxydb.h" #include "kvi_error.h" #include "kvi_out.h" #include "kvi_options.h" #include "kvi_ircsocket.h" #include "kvi_console.h" #include "kvi_netutils.h" #include "kvi_internalcmd.h" #include "kvi_frame.h" #include "kvi_mexlinkfilter.h" #include "kvi_garbage.h" #include "kvi_malloc.h" #include "kvi_memmove.h" #include "kvi_debug.h" #include "kvi_ircconnection.h" #include "kvi_ircconnectiontarget.h" #include "kvi_ircsocket.h" #include "kvi_error.h" #define __KVI_DEBUG__ #include "kvi_debug.h" #include extern KVIRC_API KviIrcServerDataBase * g_pIrcServerDataBase; extern KVIRC_API KviProxyDataBase * g_pProxyDataBase; extern KVIRC_API KviGarbageCollector * g_pGarbageCollector; KviIrcConnectionTargetResolver::KviIrcConnectionTargetResolver(KviIrcConnection * pConnection) : TQObject() { m_pConnection = pConnection; m_pTarget = 0; m_pConsole = m_pConnection->console(); m_pStartTimer = 0; m_pProxyDns = 0; m_pServerDns = 0; m_eState = Idle; m_eStatus = Success; m_iLastError = KviError_success; } KviIrcConnectionTargetResolver::~KviIrcConnectionTargetResolver() { cleanup(); } void KviIrcConnectionTargetResolver::cleanup() { if(m_pProxyDns) { if(m_pProxyDns->isRunning()) { // deleting a running dns may block // thus garbage-collect it and delete later g_pGarbageCollector->collect(m_pProxyDns); } else { // can't block : just delete it delete m_pProxyDns; } m_pProxyDns = 0; } if(m_pServerDns) { if(m_pServerDns->isRunning()) { // deleting a running dns may block // thus garbage-collect it and delete later g_pGarbageCollector->collect(m_pServerDns); } else { // can't block : just delete it delete m_pServerDns; } m_pServerDns = 0; } if(m_pStartTimer) { delete m_pStartTimer; m_pStartTimer = 0; } } void KviIrcConnectionTargetResolver::start(KviIrcConnectionTarget * t) { __ASSERT(m_eState == Idle); m_eState = Running; if(m_pStartTimer) // this should never happen I guess { delete m_pStartTimer; m_pStartTimer = 0; } m_pStartTimer = new TQTimer(this); connect(m_pStartTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(asyncStartResolve())); m_pTarget = t; m_pStartTimer->start(0); } void KviIrcConnectionTargetResolver::abort() { cleanup(); // do a cleanup to kill the timers and dns slaves if(m_eState == Terminated)return; m_pConsole->outputNoFmt(KVI_OUT_SYSTEMERROR,__tr2qs("Hostname resolution aborted")); terminate(Error,KviError_operationAborted); } void KviIrcConnectionTargetResolver::asyncStartResolve() { if(m_pStartTimer) { delete m_pStartTimer; m_pStartTimer = 0; } m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Attempting %Q to %Q (%Q) on port %u"), m_pTarget->server()->useSSL() ? &(__tr2qs("secure connection")) : &(__tr2qs("connection")), &(m_pTarget->server()->m_szHostname), &(m_pTarget->networkName()), m_pTarget->server()->m_uPort); if(m_pTarget->proxy()) { m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Attempting to 'bounce' on proxy %s on port %u (protocol %s)"), m_pTarget->proxy()->m_szHostname.ptr(), m_pTarget->proxy()->m_uPort, m_pTarget->proxy()->protocolName()); lookupProxyHostname(); } else { lookupServerHostname(); } } void KviIrcConnectionTargetResolver::lookupProxyHostname() { bool bValidIp; #ifdef COMPILE_IPV6_SUPPORT if(m_pTarget->proxy()->isIpV6()) { bValidIp = kvi_isValidStringIp_V6(m_pTarget->proxy()->m_szIp.ptr()); } else { #endif bValidIp = kvi_isValidStringIp(m_pTarget->proxy()->m_szIp.ptr()); #ifdef COMPILE_IPV6_SUPPORT } #endif if(bValidIp) { if(!_OUTPUT_QUIET) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Using cached proxy IP address (%s)"), m_pTarget->proxy()->m_szIp.ptr()); if(m_pTarget->proxy()->protocol() != KviProxy::Http && m_pTarget->proxy()->protocol() != KviProxy::Socks5) lookupServerHostname(); else terminate(Success,KviError_success); } else { #ifdef COMPILE_IPV6_SUPPORT if(m_pTarget->proxy()->isIpV6()) { bValidIp = kvi_isValidStringIp_V6(m_pTarget->proxy()->m_szHostname.ptr()); } else { #endif bValidIp = kvi_isValidStringIp(m_pTarget->proxy()->m_szHostname.ptr()); #ifdef COMPILE_IPV6_SUPPORT } #endif if(bValidIp) { m_pTarget->proxy()->m_szIp=m_pTarget->proxy()->m_szHostname; if(m_pTarget->proxy()->protocol() != KviProxy::Http && m_pTarget->proxy()->protocol() != KviProxy::Socks5) { lookupServerHostname(); } else { terminate(Success,KviError_success); } } else { if(m_pProxyDns) { tqDebug("Something weird is happening, m_pProxyDns is non-zero in lookupProxyHostname()"); delete m_pProxyDns; m_pProxyDns = 0; } m_pProxyDns = new KviDns(); connect(m_pProxyDns,TQT_SIGNAL(lookupDone(KviDns *)),this,TQT_SLOT(proxyLookupTerminated(KviDns *))); if(!m_pProxyDns->lookup(m_pTarget->proxy()->m_szHostname.ptr(), m_pTarget->proxy()->isIpV6() ? KviDns::IpV6 : KviDns::IpV4)) { m_pConsole->outputNoFmt(KVI_OUT_SYSTEMWARNING, __tr2qs("Unable to look up the IRC proxy hostname: Can't start the DNS slave")); m_pConsole->outputNoFmt(KVI_OUT_SYSTEMWARNING, __tr2qs("Resuming direct server connection")); // FIXME: #warning "Option for resuming direct connection or not ?" delete m_pProxyDns; m_pProxyDns = 0; m_pTarget->clearProxy(); lookupServerHostname(); } else { if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Looking up the proxy hostname (%s)..."), m_pTarget->proxy()->m_szHostname.ptr()); } } } } void KviIrcConnectionTargetResolver::proxyLookupTerminated(KviDns *) { if(m_pProxyDns->state() != KviDns::Success) { TQString szErr = KviError::getDescription(m_pProxyDns->error()); m_pConsole->output(KVI_OUT_SYSTEMERROR, __tr2qs("Can't find the proxy IP address: %Q"), &szErr); // FIXME: #warning "Option to resume the direct connection if proxy failed ?" m_pConsole->output(KVI_OUT_SYSTEMERROR, __tr2qs("Resuming direct server connection")); m_pTarget->clearProxy(); } else { TQString szFirstIpAddress = m_pProxyDns->firstIpAddress(); if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Proxy hostname resolved to %Q"),&szFirstIpAddress); m_pTarget->proxy()->m_szIp = m_pProxyDns->firstIpAddress(); g_pProxyDataBase->updateProxyIp(m_pTarget->proxy()->m_szIp.ptr(),szFirstIpAddress); if(m_pProxyDns->hostnameCount() > 1) { TQString szFirstHostname = m_pProxyDns->firstHostname(); for(TQString * addr = m_pProxyDns->hostnameList()->next();addr;addr = m_pProxyDns->hostnameList()->next()) { if(!_OUTPUT_QUIET) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Proxy %Q has a nickname: %Q"),&szFirstHostname,addr); } } } delete m_pProxyDns; m_pProxyDns = 0; if(m_pTarget->proxy()) { if(m_pTarget->proxy()->protocol() == KviProxy::Http || m_pTarget->proxy()->protocol() == KviProxy::Socks5) { terminate(Success,KviError_success); return; } } lookupServerHostname(); } void KviIrcConnectionTargetResolver::lookupServerHostname() { bool bValidIp; #ifdef COMPILE_IPV6_SUPPORT if(m_pTarget->server()->isIpV6()) { bValidIp = KviNetUtils::isValidStringIp_V6(m_pTarget->server()->m_szIp); } else { #endif bValidIp = KviNetUtils::isValidStringIp(m_pTarget->server()->m_szIp); #ifdef COMPILE_IPV6_SUPPORT } #endif if(bValidIp && m_pTarget->server()->cacheIp()) { if(!_OUTPUT_QUIET) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Using cached server IP address (%s)"), m_pTarget->server()->m_szIp.utf8().data()); haveServerIp(); } else { #ifdef COMPILE_IPV6_SUPPORT if(m_pTarget->server()->isIpV6()) { bValidIp = KviNetUtils::isValidStringIp_V6(m_pTarget->server()->m_szHostname); } else { #endif bValidIp = KviNetUtils::isValidStringIp(m_pTarget->server()->m_szHostname); #ifdef COMPILE_IPV6_SUPPORT } #endif if(bValidIp) { m_pTarget->server()->m_szIp=m_pTarget->server()->m_szHostname; haveServerIp(); } else { if(m_pServerDns) { tqDebug("Something weird is happening, m_pServerDns is non-zero in lookupServerHostname()"); delete m_pServerDns; m_pServerDns = 0; } m_pServerDns = new KviDns(); connect(m_pServerDns,TQT_SIGNAL(lookupDone(KviDns *)),this, TQT_SLOT(serverLookupTerminated(KviDns *))); if(!m_pServerDns->lookup(m_pTarget->server()->m_szHostname, m_pTarget->server()->isIpV6() ? KviDns::IpV6 : KviDns::IpV4)) { m_pConsole->outputNoFmt(KVI_OUT_SYSTEMERROR, __tr2qs("Unable to look up the server hostname: Can't start the DNS slave")); terminate(Error,KviError_internalError); } else { if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Looking up the server hostname (%s)..."), m_pTarget->server()->m_szHostname.utf8().data()); } } } } void KviIrcConnectionTargetResolver::serverLookupTerminated(KviDns *) { if(m_pServerDns->state() != KviDns::Success) { TQString szErr = KviError::getDescription(m_pServerDns->error()); m_pConsole->output(KVI_OUT_SYSTEMERROR, __tr2qs("Can't find the server IP address: %Q"), &szErr); #ifdef COMPILE_IPV6_SUPPORT if(!(m_pTarget->server()->isIpV6())) { m_pConsole->output(KVI_OUT_SYSTEMERROR, __tr2qs("If this server is an IPv6 one, try /server -i %Q"), &(m_pTarget->server()->m_szHostname)); } #endif terminate(Error,m_pServerDns->error()); return; } TQString szFirstIpAddress = m_pServerDns->firstIpAddress(); if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Server hostname resolved to %Q"), &szFirstIpAddress); g_pIrcServerDataBase->updateServerIp(m_pTarget->server(),szFirstIpAddress); TQString szFirstHostname = m_pServerDns->firstHostname(); if(!KviTQString::equalCI(m_pTarget->server()->m_szHostname,m_pServerDns->firstHostname())) { if(!_OUTPUT_QUIET) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Real hostname for %Q is %Q"), &(m_pTarget->server()->m_szHostname), &szFirstHostname); m_pTarget->server()->m_szHostname = szFirstHostname; } m_pTarget->server()->m_szIp = m_pServerDns->firstIpAddress(); if(m_pServerDns->hostnameCount() > 1) { for(TQString * addr = m_pServerDns->hostnameList()->next();addr;addr = m_pServerDns->hostnameList()->next()) { if(!_OUTPUT_QUIET) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Server %Q has a nickname: %Q"), &szFirstHostname,addr); } } delete m_pServerDns; m_pServerDns = 0; haveServerIp(); } bool KviIrcConnectionTargetResolver::validateLocalAddress(const TQString &szAddress,TQString &szBuffer) { // szAddress may be an ip address or an interface name #ifdef COMPILE_IPV6_SUPPORT if(m_pTarget->server()->isIpV6()) { if(KviNetUtils::isValidStringIp_V6(szAddress)) { szBuffer = szAddress; return true; } } else { #endif if(KviNetUtils::isValidStringIp(szAddress)) { szBuffer = szAddress; return true; } #ifdef COMPILE_IPV6_SUPPORT } #endif // is it an interface name ? return KviNetUtils::getInterfaceAddress(szAddress,szBuffer); } void KviIrcConnectionTargetResolver::haveServerIp() { if(KVI_OPTION_BOOL(KviOption_boolUseIdentService) && !KVI_OPTION_BOOL(KviOption_boolUseIdentServiceOnlyOnConnect)) m_pConsole->frame()->executeInternalCommand(KVI_INTERNALCOMMAND_IDENT_START); TQString bindAddress; if(m_pTarget->hasBindAddress()) { if(!validateLocalAddress(m_pTarget->bindAddress(),bindAddress)) { TQString szBindAddress = m_pTarget->bindAddress(); if((szBindAddress.find('.') != -1) || (szBindAddress.find(':') != -1)) { if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The specified bind address (%Q) is not valid"), &szBindAddress); } else { if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The specified bind address (%Q) is not valid (the interface it refers to might be down)"), &(szBindAddress)); } } } else { // the options specify a bind address ? #ifdef COMPILE_IPV6_SUPPORT if(m_pTarget->server()->isIpV6()) { if(KVI_OPTION_BOOL(KviOption_boolBindIrcIpV6ConnectionsToSpecifiedAddress)) { if(!KVI_OPTION_STRING(KviOption_stringIpV6ConnectionBindAddress).isEmpty()) { if(!validateLocalAddress(KVI_OPTION_STRING(KviOption_stringIpV6ConnectionBindAddress),bindAddress)) { // if it is not an interface name , kill it for now and let the user correct the address if(KVI_OPTION_STRING(KviOption_stringIpV6ConnectionBindAddress).find(':') != -1) { if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The system-wide IPv6 bind address (%s) is not valid"), KVI_OPTION_STRING(KviOption_stringIpV6ConnectionBindAddress).utf8().data()); KVI_OPTION_BOOL(KviOption_boolBindIrcIpV6ConnectionsToSpecifiedAddress) = false; } else { // this is an interface address: might be down if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The system-wide IPv6 bind address (%s) is not valid (the interface it refers to might be down)"), KVI_OPTION_STRING(KviOption_stringIpV6ConnectionBindAddress).utf8().data()); } } } else { // empty address....kill it KVI_OPTION_BOOL(KviOption_boolBindIrcIpV6ConnectionsToSpecifiedAddress) = false; } } } else { #endif if(KVI_OPTION_BOOL(KviOption_boolBindIrcIpV4ConnectionsToSpecifiedAddress)) { if(!KVI_OPTION_STRING(KviOption_stringIpV4ConnectionBindAddress).isEmpty()) { if(!validateLocalAddress(KVI_OPTION_STRING(KviOption_stringIpV4ConnectionBindAddress),bindAddress)) { // if it is not an interface name , kill it for now and let the user correct the address if(KVI_OPTION_STRING(KviOption_stringIpV4ConnectionBindAddress).find(':') != -1) { if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The system-wide IPv4 bind address (%s) is not valid"), KVI_OPTION_STRING(KviOption_stringIpV4ConnectionBindAddress).utf8().data()); KVI_OPTION_BOOL(KviOption_boolBindIrcIpV4ConnectionsToSpecifiedAddress) = false; } else { // this is an interface address: might be down if(!_OUTPUT_MUTE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("The system-wide IPv4 bind address (%s) is not valid (the interface it refers to might be down)"), KVI_OPTION_STRING(KviOption_stringIpV4ConnectionBindAddress).utf8().data()); } } } else { // empty address....kill it KVI_OPTION_BOOL(KviOption_boolBindIrcIpV4ConnectionsToSpecifiedAddress) = false; } } #ifdef COMPILE_IPV6_SUPPORT } #endif } m_pTarget->setBindAddress(bindAddress); terminate(Success,KviError_success); } void KviIrcConnectionTargetResolver::terminate(Status s,int iLastError) { __ASSERT(m_eState != Terminated); cleanup(); // do a cleanup anyway m_eState = Terminated; m_eStatus = s; m_iLastError = iLastError; emit terminated(); }