summaryrefslogtreecommitdiffstats
path: root/src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp')
-rw-r--r--src/kvirc/kernel/kvi_ircconnectiontargetresolver.cpp555
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();
+}