summaryrefslogtreecommitdiffstats
path: root/src/kvilib/net/kvi_dns.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kvilib/net/kvi_dns.cpp')
-rw-r--r--src/kvilib/net/kvi_dns.cpp450
1 files changed, 450 insertions, 0 deletions
diff --git a/src/kvilib/net/kvi_dns.cpp b/src/kvilib/net/kvi_dns.cpp
new file mode 100644
index 00000000..faa2e126
--- /dev/null
+++ b/src/kvilib/net/kvi_dns.cpp
@@ -0,0 +1,450 @@
+//=============================================================================
+//
+// File : kvi_dns.cpp
+// Creation date : Sat Jul 21 2000 17:19:31 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2000-2007 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 __KVILIB__
+
+#include "kvi_dns.h"
+#include "kvi_error.h"
+#include "kvi_netutils.h"
+
+#include <errno.h>
+
+#ifdef COMPILE_ON_WINDOWS
+ #include <winsock2.h>
+
+ #ifdef COMPILE_IPV6_SUPPORT
+ #ifdef WIN2K
+ #include <ws2ip6.h>
+ #else
+ #include <ws2tcpip.h>
+ //#include <tpipv6.h>
+ #endif
+ #endif
+#else
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netdb.h>
+#endif
+
+// this is for FreeBSD
+#ifndef EAI_ADDRFAMILY
+ #define EAI_ADDRFAMILY EAI_FAMILY
+#endif
+
+#ifndef EAI_NODATA
+ #define EAI_NODATA 0
+#endif
+
+
+
+KviDnsResult::KviDnsResult()
+{
+ m_iError = KviError_success;
+ m_pHostnameList = new KviPointerList<QString>;
+ m_pHostnameList->setAutoDelete(true);
+ m_pIpAddressList = new KviPointerList<QString>;
+ m_pIpAddressList->setAutoDelete(true);
+
+}
+
+KviDnsResult::~KviDnsResult()
+{
+ delete m_pHostnameList;
+ delete m_pIpAddressList;
+}
+
+void KviDnsResult::appendHostname(const QString &host)
+{
+ m_pHostnameList->append(new QString(host));
+}
+
+
+void KviDnsResult::appendAddress(const QString &addr)
+{
+ m_pIpAddressList->append(new QString(addr));
+}
+
+
+
+KviDnsThread::KviDnsThread(KviDns * pDns)
+{
+ m_pParentDns = pDns;
+}
+
+KviDnsThread::~KviDnsThread()
+{
+}
+
+int KviDnsThread::translateDnsError(int iErr)
+{
+#if defined(COMPILE_IPV6_SUPPORT) || !defined(COMPILE_ON_WINDOWS)
+
+ switch(iErr)
+ {
+ case EAI_FAMILY: return KviError_unsupportedAddressFamily; break;
+#if !defined(COMPILE_ON_WINDOWS) && defined(EAI_ADDRFAMILY) && (EAI_ADDRFAMILY != EAI_FAMILY)
+ case EAI_ADDRFAMILY: return KviError_unsupportedAddressFamily; break;
+#endif
+// NOT FreeBSD ARE WE?
+#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
+// YARR
+ case EAI_NODATA: return KviError_validNameButNoIpAddress; break;
+#endif
+ case EAI_FAIL: return KviError_unrecoverableNameserverError; break;
+ case EAI_AGAIN: return KviError_dnsTemporaneousFault; break;
+ // this should never happen
+ case EAI_BADFLAGS: return KviError_dnsInternalErrorBadFlags; break;
+ case EAI_MEMORY: return KviError_dnsInternalErrorOutOfMemory; break;
+ // got this when experimenting with protocols
+ case EAI_SERVICE: return KviError_dnsInternalErrorServiceNotSupported; break;
+#ifndef COMPILE_ON_WINDOWS
+ case EAI_NONAME: return KviError_dnsNoName; break;
+#endif
+ // got this when experimenting with protocols
+ case EAI_SOCKTYPE: return KviError_dnsInternalErrorUnsupportedSocketType; break;
+#ifndef COMPILE_ON_WINDOWS
+ case EAI_SYSTEM: return -errno;
+#endif
+ }
+
+#endif
+ return KviError_dnsQueryFailed;
+}
+
+void KviDnsThread::postDnsError(KviDnsResult * dns,int iErr)
+{
+ dns->setError(iErr);
+ KviThreadDataEvent<KviDnsResult> * e = new KviThreadDataEvent<KviDnsResult>(KVI_DNS_THREAD_EVENT_DATA);
+ e->setData(dns);
+ postEvent(m_pParentDns,e);
+}
+
+void KviDnsThread::run()
+{
+ KviDnsResult * dns = new KviDnsResult();
+
+ dns->setQuery(m_szQuery);
+
+ if(m_szQuery.isEmpty())
+ {
+ postDnsError(dns,KviError_noHostToResolve);
+ return;
+ }
+
+#ifndef COMPILE_IPV6_SUPPORT
+ if(m_queryType != KviDns::IpV4)
+ {
+ if(m_queryType == KviDns::IpV6)
+ {
+ postDnsError(dns,KviError_noIpV6Support);
+ return;
+ }
+ m_queryType = KviDns::IpV4;
+ }
+#endif
+
+#if defined(COMPILE_ON_WINDOWS) && !defined(COMPILE_IPV6_SUPPORT)
+
+ if(m_queryType == KviDns::IpV6)
+ {
+ postDnsError(dns,KviError_noIpV6Support);
+ return;
+ }
+
+ // gethostbyaddr and gethostbyname are thread-safe on Windoze
+ struct in_addr inAddr;
+ struct hostent *pHostEntry = 0;
+
+
+ // DIE DIE!....I hope that this stuff will disappear sooner or later :)
+
+ if(KviNetUtils::stringIpToBinaryIp(m_szQuery,&inAddr))
+ {
+ pHostEntry = gethostbyaddr((const char *)&inAddr,sizeof(inAddr),AF_INET);
+ } else {
+ pHostEntry = gethostbyname(m_szQuery);
+ }
+
+ if(!pHostEntry)
+ {
+ switch(h_errno)
+ {
+ case HOST_NOT_FOUND: dns->setError(KviError_hostNotFound); break;
+ case NO_ADDRESS: dns->setError(KviError_validNameButNoIpAddress); break;
+ case NO_RECOVERY: dns->setError(KviError_unrecoverableNameserverError); break;
+ case TRY_AGAIN: dns->setError(KviError_dnsTemporaneousFault); break;
+ default: dns->setError(KviError_dnsQueryFailed); break;
+ }
+ } else {
+ dns->appendHostname(pHostEntry->h_name);
+ QString szIp;
+ KviNetUtils::binaryIpToStringIp(* ((struct in_addr*)(pHostEntry->h_addr)),szIp);
+ dns->appendAddress(szIp);
+
+ int idx = 1;
+ while(pHostEntry->h_addr_list[idx])
+ {
+ QString tmp;
+ KviNetUtils::binaryIpToStringIp(* ((struct in_addr*)(pHostEntry->h_addr_list[idx])),tmp);
+ if(tmp.hasData())dns->appendAddress(tmp);
+ ++idx;
+ }
+ if(pHostEntry->h_aliases[0])
+ {
+ dns->appendHostname(QString::fromUtf8(pHostEntry->h_aliases[0]));
+ if(pHostEntry->h_aliases[1])dns->appendHostname(QString::fromUtf8(pHostEntry->h_aliases[1]));
+ }
+ }
+
+
+#else //!COMPILE_ON_WINDOWS || COMPILE_IPV6_SUPPORT
+
+ int retVal;
+
+
+//#ifdef HAVE_GETNAMEINFO
+ struct sockaddr_in ipv4Addr;
+
+#ifdef COMPILE_IPV6_SUPPORT
+ struct sockaddr_in6 ipv6Addr;
+ bool bIsIpV6Ip = false;
+#endif
+
+ bool bIsIpV4Ip = KviNetUtils::stringIpToBinaryIp(m_szQuery,(struct in_addr *)&(ipv4Addr.sin_addr));
+
+#ifdef COMPILE_IPV6_SUPPORT
+ if(!bIsIpV4Ip)bIsIpV6Ip = KviNetUtils::stringIpToBinaryIp_V6(m_szQuery,(struct in6_addr *)&(ipv6Addr.sin6_addr));
+#endif
+
+//#ifdef HAVE_GETNAMEINFO
+
+#ifdef COMPILE_IPV6_SUPPORT
+ if(bIsIpV4Ip || bIsIpV6Ip)
+ {
+#else
+ if(bIsIpV4Ip)
+ {
+#endif
+ // use getnameinfo...
+ char retname[1025]; // should be enough....
+
+#ifdef COMPILE_IPV6_SUPPORT
+ if(bIsIpV4Ip)
+ {
+#endif
+ ipv4Addr.sin_family = AF_INET;
+ ipv4Addr.sin_port = 0;
+ // NI_NAMEREQD as last param ?
+ retVal = getnameinfo((struct sockaddr *)&ipv4Addr,sizeof(ipv4Addr),retname,1025,0,0,NI_NAMEREQD);
+#ifdef COMPILE_IPV6_SUPPORT
+ } else {
+ ipv6Addr.sin6_family = AF_INET6;
+ ipv6Addr.sin6_port = 0;
+ retVal = getnameinfo((struct sockaddr *)&ipv6Addr,sizeof(ipv6Addr),retname,1025,0,0,NI_NAMEREQD);
+ }
+#endif
+
+ if(retVal != 0)dns->setError(translateDnsError(retVal));
+ else {
+ dns->appendHostname(retname);
+ dns->appendAddress(m_szQuery);
+ }
+
+ } else {
+//#endif //HAVE_GETNAMEINFO
+
+
+//#ifdef COMPILE_IPV6_SUPPORT
+// struct in6_addr in6Addr;
+//#endif
+ struct addrinfo * pRet = 0;
+ struct addrinfo * pNext;
+ struct addrinfo hints;
+ hints.ai_flags = 0; //AI_CANONNAME; <-- for IPV6 it makes cannoname to point to the IP address!
+#ifdef COMPILE_IPV6_SUPPORT
+ hints.ai_family = (m_queryType == KviDns::IpV6) ? PF_INET6 : ((m_queryType == KviDns::IpV4) ? PF_INET : PF_UNSPEC);
+#else
+ hints.ai_family = PF_INET;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ hints.ai_addrlen = 0;
+ hints.ai_canonname = 0;
+ hints.ai_addr = 0;
+ hints.ai_next = 0;
+
+ retVal = getaddrinfo(KviQString::toUtf8(m_szQuery).data(),0,&hints,&pRet);
+
+ if(retVal != 0)dns->setError(translateDnsError(retVal));
+ else {
+ dns->appendHostname(pRet->ai_canonname ? QString::fromUtf8(pRet->ai_canonname) : m_szQuery);
+ QString szIp;
+#ifdef COMPILE_IPV6_SUPPORT
+ if(pRet->ai_family == PF_INET6)KviNetUtils::binaryIpToStringIp_V6(((sockaddr_in6 *)(pRet->ai_addr))->sin6_addr,szIp);
+ else {
+#endif
+ KviNetUtils::binaryIpToStringIp(((sockaddr_in *)(pRet->ai_addr))->sin_addr,szIp);
+#ifdef COMPILE_IPV6_SUPPORT
+ }
+#endif
+ dns->appendAddress(szIp);
+
+ pNext = pRet->ai_next;
+ while(pNext)
+ {
+ QString tmp;
+#ifdef COMPILE_IPV6_SUPPORT
+ if(pNext->ai_family == PF_INET6)KviNetUtils::binaryIpToStringIp_V6(((sockaddr_in6 *)(pNext->ai_addr))->sin6_addr,tmp);
+ else {
+#endif
+ KviNetUtils::binaryIpToStringIp(((sockaddr_in *)(pNext->ai_addr))->sin_addr,tmp);
+#ifdef COMPILE_IPV6_SUPPORT
+ }
+#endif
+ if(!tmp.isEmpty())dns->appendAddress(tmp);
+
+ if(pNext->ai_canonname)
+ {
+ // FIXME: only of not equal to other names ?
+ dns->appendHostname(QString::fromUtf8(pNext->ai_canonname));
+ }
+
+ pNext = pNext->ai_next;
+
+ }
+ }
+ if(pRet)freeaddrinfo(pRet);
+//#ifdef HAVE_GETNAMEINFO
+ }
+//#endif //HAVE_GETNAMEINFO
+
+#endif // !COMPILE_ON_WINDOWS
+
+
+ KviThreadDataEvent<KviDnsResult> * e = new KviThreadDataEvent<KviDnsResult>(KVI_DNS_THREAD_EVENT_DATA);
+ e->setData(dns);
+ postEvent(m_pParentDns,e);
+}
+
+
+
+
+KviDns::KviDns()
+: QObject()
+{
+ m_pSlaveThread = new KviDnsThread(this);
+ m_pDnsResult = new KviDnsResult();
+ m_pAuxData = 0;
+ m_state = Idle;
+}
+
+KviDns::~KviDns()
+{
+ if(m_pSlaveThread)delete m_pSlaveThread; // will eventually terminate it (but it will also block us!!!)
+ KviThreadManager::killPendingEvents(this);
+ if(m_pDnsResult)delete m_pDnsResult;
+ if(m_pAuxData)debug("You're leaking memory man! m_pAuxData is non 0!");
+}
+
+
+bool KviDns::isRunning() const
+{
+ return (m_state == Busy);
+};
+
+bool KviDns::lookup(const QString &query,QueryType type)
+{
+ if(m_state == Busy)return false;
+ m_pSlaveThread->setQuery(KviQString::trimmed(query),type);
+ bool bStarted = m_pSlaveThread->start();
+ m_state = bStarted ? Busy : Failure;
+ return bStarted;
+}
+
+int KviDns::error()
+{
+ if(!m_pDnsResult)return KviError_dnsQueryFailed;
+ return m_pDnsResult->error();
+}
+
+KviDnsResult * KviDns::result()
+{
+ if(!m_pDnsResult)m_pDnsResult = new KviDnsResult();
+ return m_pDnsResult;
+}
+
+KviPointerList<QString> * KviDns::hostnameList()
+{
+ return result()->hostnameList();
+}
+
+KviPointerList<QString> * KviDns::ipAddressList()
+{
+ return result()->ipAddressList();
+}
+
+int KviDns::hostnameCount()
+{
+ return result()->hostnameList()->count();
+}
+
+int KviDns::ipAddressCount()
+{
+ return result()->ipAddressList()->count();
+}
+
+const QString & KviDns::firstHostname()
+{
+ QString * pStr = result()->hostnameList()->first();
+ if(pStr)return *pStr;
+ return KviQString::empty;
+}
+
+const QString & KviDns::firstIpAddress()
+{
+ QString * pStr = result()->ipAddressList()->first();
+ if(pStr)return *pStr;
+ return KviQString::empty;
+}
+
+const QString & KviDns::query()
+{
+ return result()->query();
+}
+
+bool KviDns::event(QEvent *e)
+{
+ if(e->type() == KVI_THREAD_EVENT)
+ {
+ if(((KviThreadEvent *)e)->id() == KVI_DNS_THREAD_EVENT_DATA)
+ {
+ if(m_pDnsResult)delete m_pDnsResult;
+ m_pDnsResult = ((KviThreadDataEvent<KviDnsResult> *)e)->getData();
+ m_state = (m_pDnsResult->error() == KviError_success) ? Success : Failure;
+ emit lookupDone(this);
+ return true;
+ } // else ops... unknown thread event ?
+ }
+ return QObject::event(e);
+}
+