diff options
Diffstat (limited to 'src/kvilib/net/kvi_dns.cpp')
-rw-r--r-- | src/kvilib/net/kvi_dns.cpp | 450 |
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); +} + |