//============================================================================= // // 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 #ifdef COMPILE_ON_WINDOWS #include #ifdef COMPILE_IPV6_SUPPORT #ifdef WIN2K #include #else #include //#include #endif #endif #else #include #include #include #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; m_pHostnameList->setAutoDelete(true); m_pIpAddressList = new KviPointerList; m_pIpAddressList->setAutoDelete(true); } KviDnsResult::~KviDnsResult() { delete m_pHostnameList; delete m_pIpAddressList; } void KviDnsResult::appendHostname(const TQString &host) { m_pHostnameList->append(new TQString(host)); } void KviDnsResult::appendAddress(const TQString &addr) { m_pIpAddressList->append(new TQString(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 * e = new KviThreadDataEvent(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); TQString szIp; KviNetUtils::binaryIpToStringIp(* ((struct in_addr*)(pHostEntry->h_addr)),szIp); dns->appendAddress(szIp); int idx = 1; while(pHostEntry->h_addr_list[idx]) { TQString 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(TQString::fromUtf8(pHostEntry->h_aliases[0])); if(pHostEntry->h_aliases[1])dns->appendHostname(TQString::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(KviTQString::toUtf8(m_szQuery).data(),0,&hints,&pRet); if(retVal != 0)dns->setError(translateDnsError(retVal)); else { dns->appendHostname(pRet->ai_canonname ? TQString::fromUtf8(pRet->ai_canonname) : m_szQuery); TQString 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) { TQString 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(TQString::fromUtf8(pNext->ai_canonname)); } pNext = pNext->ai_next; } } if(pRet)freeaddrinfo(pRet); //#ifdef HAVE_GETNAMEINFO } //#endif //HAVE_GETNAMEINFO #endif // !COMPILE_ON_WINDOWS KviThreadDataEvent * e = new KviThreadDataEvent(KVI_DNS_THREAD_EVENT_DATA); e->setData(dns); postEvent(m_pParentDns,e); } KviDns::KviDns() : TQObject() { 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 TQString &query,QueryType type) { if(m_state == Busy)return false; m_pSlaveThread->setQuery(KviTQString::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 * KviDns::hostnameList() { return result()->hostnameList(); } KviPointerList * KviDns::ipAddressList() { return result()->ipAddressList(); } int KviDns::hostnameCount() { return result()->hostnameList()->count(); } int KviDns::ipAddressCount() { return result()->ipAddressList()->count(); } const TQString & KviDns::firstHostname() { TQString * pStr = result()->hostnameList()->first(); if(pStr)return *pStr; return KviTQString::empty; } const TQString & KviDns::firstIpAddress() { TQString * pStr = result()->ipAddressList()->first(); if(pStr)return *pStr; return KviTQString::empty; } const TQString & KviDns::query() { return result()->query(); } bool KviDns::event(TQEvent *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 *)e)->getData(); m_state = (m_pDnsResult->error() == KviError_success) ? Success : Failure; emit lookupDone(this); return true; } // else ops... unknown thread event ? } return TQObject::event(e); }