diff options
Diffstat (limited to 'src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp')
-rw-r--r-- | src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp b/src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp new file mode 100644 index 00000000..824993bb --- /dev/null +++ b/src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp @@ -0,0 +1,555 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// 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 <qtimer.h> + +extern KVIRC_API KviIrcServerDataBase * g_pIrcServerDataBase; +extern KVIRC_API KviProxyDataBase * g_pProxyDataBase; +extern KVIRC_API KviGarbageCollector * g_pGarbageCollector; + + + +KviIrcConnectionTargetResolver::KviIrcConnectionTargetResolver(KviIrcConnection * pConnection) +: QObject() +{ + 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 QTimer(this); + connect(m_pStartTimer,SIGNAL(timeout()),this,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) + { + debug("Something weird is happening, m_pProxyDns is non-zero in lookupProxyHostname()"); + delete m_pProxyDns; + m_pProxyDns = 0; + } + + m_pProxyDns = new KviDns(); + connect(m_pProxyDns,SIGNAL(lookupDone(KviDns *)),this,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) + { + QString 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 { + QString 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) + { + QString szFirstHostname = m_pProxyDns->firstHostname(); + + for(QString * 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) + { + debug("Something weird is happening, m_pServerDns is non-zero in lookupServerHostname()"); + delete m_pServerDns; + m_pServerDns = 0; + } + m_pServerDns = new KviDns(); + connect(m_pServerDns,SIGNAL(lookupDone(KviDns *)),this, + 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) + { + QString 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; + } + QString 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); + + QString szFirstHostname = m_pServerDns->firstHostname(); + + if(!KviQString::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(QString * 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 QString &szAddress,QString &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); + + QString bindAddress; + + if(m_pTarget->hasBindAddress()) + { + if(!validateLocalAddress(m_pTarget->bindAddress(),bindAddress)) + { + QString 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(); +} |