|
|
|
/* -*- C++ -*-
|
|
|
|
* Copyright (C) 2003,2004 Thiago Macieira <thiago.macieira@kdemail.net>
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included
|
|
|
|
* in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_NET_IF_H
|
|
|
|
#include <net/if.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <tqthread.h>
|
|
|
|
#include <tqmutex.h>
|
|
|
|
#include <tqstrlist.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
|
|
|
|
#include "kdebug.h"
|
|
|
|
#include "kglobal.h"
|
|
|
|
#include "kstandarddirs.h"
|
|
|
|
#include "kapplication.h"
|
|
|
|
|
|
|
|
#include "kresolver.h"
|
|
|
|
#include "ksocketaddress.h"
|
|
|
|
#include "kresolverstandardworkers_p.h"
|
|
|
|
|
|
|
|
struct hostent;
|
|
|
|
struct addrinfo;
|
|
|
|
|
|
|
|
using namespace KNetwork;
|
|
|
|
using namespace KNetwork::Internal;
|
|
|
|
|
|
|
|
static bool hasIPv6()
|
|
|
|
{
|
|
|
|
#ifndef AF_INET6
|
|
|
|
return false;
|
|
|
|
#else
|
|
|
|
if (getenv("KDE_NO_IPV6") != 0L)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int fd = ::socket(AF_INET6, SOCK_STREAM, 0);
|
|
|
|
if (fd == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
::close(fd);
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// blacklist management
|
|
|
|
static TQMutex blacklistMutex; // KDE4: change to a QReadWriteLock
|
|
|
|
TQStringList KBlacklistWorker::blacklist;
|
|
|
|
|
|
|
|
void KBlacklistWorker::init()
|
|
|
|
{
|
|
|
|
// HACK!
|
|
|
|
// FIXME KDE4: How do I detect there is an instance, without triggering
|
|
|
|
// its creation or an assertion fault?
|
|
|
|
if (!KGlobal::_instance)
|
|
|
|
return;
|
|
|
|
|
|
|
|
static bool beenhere = false;
|
|
|
|
|
|
|
|
if (beenhere)
|
|
|
|
return;
|
|
|
|
|
|
|
|
beenhere = true;
|
|
|
|
loadBlacklist();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KBlacklistWorker::loadBlacklist()
|
|
|
|
{
|
|
|
|
TQMutexLocker locker(&blacklistMutex);
|
|
|
|
TQStringList filelist = KGlobal::dirs()->findAllResources("config", "ipv6blacklist");
|
|
|
|
|
|
|
|
TQStringList::ConstIterator it = filelist.constBegin(),
|
|
|
|
end = filelist.constEnd();
|
|
|
|
for ( ; it != end; ++it)
|
|
|
|
{
|
|
|
|
// for each file, each line is a domainname to be blacklisted
|
|
|
|
TQFile f(*it);
|
|
|
|
if (!f.open(IO_ReadOnly))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
TQTextStream stream(&f);
|
|
|
|
stream.setEncoding(TQTextStream::Latin1);
|
|
|
|
for (TQString line = stream.readLine(); !line.isNull();
|
|
|
|
line = stream.readLine())
|
|
|
|
{
|
|
|
|
if (line.isEmpty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// make sure there are no surrounding whitespaces
|
|
|
|
// and that it starts with .
|
|
|
|
line = line.stripWhiteSpace();
|
|
|
|
if (line[0] != '.')
|
|
|
|
line.prepend('.');
|
|
|
|
|
|
|
|
blacklist.append(line.lower());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// checks the blacklist to see if the domain is listed
|
|
|
|
// it matches the domain ending part
|
|
|
|
bool KBlacklistWorker::isBlacklisted(const TQString& host)
|
|
|
|
{
|
|
|
|
KBlacklistWorker::init();
|
|
|
|
|
|
|
|
// empty hostnames cannot be blacklisted
|
|
|
|
if (host.isEmpty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// KDE4: QLatin1String
|
|
|
|
TQString ascii = TQString::tqfromLatin1(KResolver::domainToAscii(host));
|
|
|
|
|
|
|
|
TQMutexLocker locker(&blacklistMutex);
|
|
|
|
|
|
|
|
// now find out if this hostname is present
|
|
|
|
TQStringList::ConstIterator it = blacklist.constBegin(),
|
|
|
|
end = blacklist.constEnd();
|
|
|
|
for ( ; it != end; ++it)
|
|
|
|
if (ascii.endsWith(*it))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// no match:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KBlacklistWorker::preprocess()
|
|
|
|
{
|
|
|
|
if (isBlacklisted(nodeName()))
|
|
|
|
{
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
finished();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KBlacklistWorker::run()
|
|
|
|
{
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
finished();
|
|
|
|
return false; // resolution failure
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Note on the use of the system resolver functions:
|
|
|
|
*
|
|
|
|
* In all cases, we prefer to use the new getaddrinfo(3) call. That means
|
|
|
|
* it will always be used if it is found.
|
|
|
|
*
|
|
|
|
* If it's not found, we have the option to use gethostbyname2_r,
|
|
|
|
* gethostbyname_r, gethostbyname2 and gethostbyname. If gethostbyname2_r
|
|
|
|
* is defined, we will use it.
|
|
|
|
*
|
|
|
|
* If it's not defined, we have to choose between the non-reentrant
|
|
|
|
* gethostbyname2 and the reentrant but IPv4-only gethostbyname_r:
|
|
|
|
* we will choose gethostbyname2 if AF_INET6 is defined.
|
|
|
|
*
|
|
|
|
* Lastly, gethostbyname will be used if nothing else is present.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef HAVE_GETADDRINFO
|
|
|
|
|
|
|
|
# if defined(HAVE_GETHOSTBYNAME2_R)
|
|
|
|
# define USE_GETHOSTBYNAME2_R
|
|
|
|
# elif defined(HAVE_GETHOSTBYNAME_R) && (!defined(AF_INET6) || !defined(HAVE_GETHOSTBYNAME2))
|
|
|
|
# define USE_GETHOSTBYNAME_R
|
|
|
|
# elif defined(HAVE_GETHOSTBYNAME2)
|
|
|
|
# define USE_GETHOSTBYNAME2)
|
|
|
|
# else
|
|
|
|
# define USE_GETHOSTBYNAME
|
|
|
|
# endif
|
|
|
|
|
|
|
|
class GetHostByNameThread: public KResolverWorkerBase
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TQCString m_hostname; // might be different!
|
|
|
|
TQ_UINT16 m_port;
|
|
|
|
int m_scopeid;
|
|
|
|
int m_af;
|
|
|
|
KResolverResults& results;
|
|
|
|
|
|
|
|
GetHostByNameThread(const char * hostname, TQ_UINT16 port,
|
|
|
|
int scopeid, int af, KResolverResults* res) :
|
|
|
|
m_hostname(hostname), m_port(port), m_scopeid(scopeid), m_af(af),
|
|
|
|
results(*res)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
~GetHostByNameThread()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual bool preprocess()
|
|
|
|
{ return true; }
|
|
|
|
|
|
|
|
virtual bool run();
|
|
|
|
|
|
|
|
void processResults(hostent* he, int my_h_errno);
|
|
|
|
};
|
|
|
|
|
|
|
|
bool GetHostByNameThread::run()
|
|
|
|
{
|
|
|
|
|
|
|
|
hostent *resultptr;
|
|
|
|
hostent my_results;
|
|
|
|
unsigned buflen = 1024;
|
|
|
|
int res;
|
|
|
|
int my_h_errno;
|
|
|
|
char *buf = 0L;
|
|
|
|
|
|
|
|
// qDebug("ResolveThread::run(): started threaded gethostbyname for %s (af = %d)",
|
|
|
|
// m_hostname.data(), m_af);
|
|
|
|
|
|
|
|
ResolverLocker resLock( this );
|
|
|
|
do
|
|
|
|
{
|
|
|
|
res = 0;
|
|
|
|
my_h_errno = HOST_NOT_FOUND;
|
|
|
|
|
|
|
|
// check blacklist
|
|
|
|
if (m_af != AF_INET &&
|
|
|
|
KBlacklistWorker::isBlacklisted(TQString::tqfromLatin1(m_hostname)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
# ifdef USE_GETHOSTBYNAME2_R
|
|
|
|
buf = new char[buflen];
|
|
|
|
res = gethostbyname2_r(m_hostname, m_af, &my_results, buf, buflen,
|
|
|
|
&resultptr, &my_h_errno);
|
|
|
|
|
|
|
|
# elif defined(USE_GETHOSTBYNAME_R)
|
|
|
|
if (m_af == AF_INET)
|
|
|
|
{
|
|
|
|
buf = new char[buflen];
|
|
|
|
res = gethostbyname_r(m_hostname, &my_results, buf, buflen,
|
|
|
|
&resultptr, &my_h_errno);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
resultptr = 0; // signal error
|
|
|
|
|
|
|
|
# elif defined(USE_GETHOSTBYNAME2)
|
|
|
|
// must lock mutex
|
|
|
|
resultptr = gethostbyname2(m_hostname, m_af);
|
|
|
|
my_h_errno = h_errno;
|
|
|
|
|
|
|
|
# else
|
|
|
|
if (m_af == AF_INET)
|
|
|
|
{
|
|
|
|
// must lock mutex
|
|
|
|
resultptr = gethostbyname(m_hostname);
|
|
|
|
my_h_errno = h_errno;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
resultptr = 0;
|
|
|
|
# endif
|
|
|
|
|
|
|
|
if (resultptr != 0L)
|
|
|
|
my_h_errno = 0;
|
|
|
|
// qDebug("GetHostByNameThread::run(): gethostbyname for %s (af = %d) returned: %d",
|
|
|
|
// m_hostname.data(), m_af, my_h_errno);
|
|
|
|
|
|
|
|
if (res == ERANGE)
|
|
|
|
{
|
|
|
|
// Enlarge the buffer
|
|
|
|
buflen += 1024;
|
|
|
|
delete [] buf;
|
|
|
|
buf = new char[buflen];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((res == ERANGE || my_h_errno != 0) && checkResolver())
|
|
|
|
{
|
|
|
|
// resolver needs updating, so we might as well do it now
|
|
|
|
resLock.openClose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (res == ERANGE);
|
|
|
|
processResults(resultptr, my_h_errno);
|
|
|
|
|
|
|
|
delete [] buf;
|
|
|
|
|
|
|
|
finished();
|
|
|
|
return results.error() == KResolver::NoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetHostByNameThread::processResults(hostent *he, int herrno)
|
|
|
|
{
|
|
|
|
if (herrno)
|
|
|
|
{
|
|
|
|
qDebug("KStandardWorker::processResults: got error %d", herrno);
|
|
|
|
switch (herrno)
|
|
|
|
{
|
|
|
|
case HOST_NOT_FOUND:
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRY_AGAIN:
|
|
|
|
results.setError(KResolver::TryAgain);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case NO_RECOVERY:
|
|
|
|
results.setError(KResolver::NonRecoverable);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case NO_ADDRESS:
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
results.setError(KResolver::UnknownError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (he == 0L)
|
|
|
|
{
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
return; // this was an error
|
|
|
|
}
|
|
|
|
|
|
|
|
// clear any errors
|
|
|
|
setError(KResolver::NoError);
|
|
|
|
results.setError(KResolver::NoError);
|
|
|
|
|
|
|
|
// we process results in the reverse order
|
|
|
|
// that is, we prepend each result to the list of results
|
|
|
|
int proto = protocol();
|
|
|
|
int socktype = socketType();
|
|
|
|
if (socktype == 0)
|
|
|
|
socktype = SOCK_STREAM; // default
|
|
|
|
|
|
|
|
TQString canon = KResolver::domainToUnicode(TQString::tqfromLatin1(he->h_name));
|
|
|
|
KInetSocketAddress sa;
|
|
|
|
sa.setPort(m_port);
|
|
|
|
if (he->h_addrtype != AF_INET)
|
|
|
|
sa.setScopeId(m_scopeid); // this will also change the socket into IPv6
|
|
|
|
|
|
|
|
for (int i = 0; he->h_addr_list[i]; i++)
|
|
|
|
{
|
|
|
|
sa.setHost(KIpAddress(he->h_addr_list[i], he->h_addrtype == AF_INET ? 4 : 6));
|
|
|
|
results.prepend(KResolverEntry(sa, socktype, proto, canon, m_hostname));
|
|
|
|
// qDebug("KStandardWorker::processResults: adding %s", sa.toString().latin1());
|
|
|
|
}
|
|
|
|
// qDebug("KStandardWorker::processResults: added %d entries", i);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else // HAVE_GETADDRINFO
|
|
|
|
|
|
|
|
class GetAddrInfoThread: public KResolverWorkerBase
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TQCString m_node;
|
|
|
|
TQCString m_serv;
|
|
|
|
int m_af;
|
|
|
|
int m_flags;
|
|
|
|
KResolverResults& results;
|
|
|
|
|
|
|
|
GetAddrInfoThread(const char* node, const char* serv, int af, int flags,
|
|
|
|
KResolverResults* res) :
|
|
|
|
m_node(node), m_serv(serv), m_af(af), m_flags(flags), results(*res)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
~GetAddrInfoThread()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual bool preprocess()
|
|
|
|
{ return true; }
|
|
|
|
|
|
|
|
virtual bool run();
|
|
|
|
|
|
|
|
void processResults(addrinfo* ai, int ret_code, KResolverResults& rr);
|
|
|
|
};
|
|
|
|
|
|
|
|
bool GetAddrInfoThread::run()
|
|
|
|
{
|
|
|
|
// check blacklist
|
|
|
|
if ((m_af != AF_INET && m_af != AF_UNSPEC) &&
|
|
|
|
KBlacklistWorker::isBlacklisted(TQString::tqfromLatin1(m_node)))
|
|
|
|
{
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
finished();
|
|
|
|
return false; // failed
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ResolverLocker resLock( this );
|
|
|
|
|
|
|
|
// process hints
|
|
|
|
addrinfo hint;
|
|
|
|
memset(&hint, 0, sizeof(hint));
|
|
|
|
hint.ai_family = m_af;
|
|
|
|
hint.ai_socktype = socketType();
|
|
|
|
hint.ai_protocol = protocol();
|
|
|
|
|
|
|
|
if (hint.ai_socktype == 0)
|
|
|
|
hint.ai_socktype = SOCK_STREAM; // default
|
|
|
|
|
|
|
|
if (m_flags & KResolver::Passive)
|
|
|
|
hint.ai_flags |= AI_PASSIVE;
|
|
|
|
if (m_flags & KResolver::CanonName)
|
|
|
|
hint.ai_flags |= AI_CANONNAME;
|
|
|
|
# ifdef AI_NUMERICHOST
|
|
|
|
if (m_flags & KResolver::NoResolve)
|
|
|
|
hint.ai_flags |= AI_NUMERICHOST;
|
|
|
|
# endif
|
|
|
|
# ifdef AI_ADDRCONFIG
|
|
|
|
hint.ai_flags |= AI_ADDRCONFIG;
|
|
|
|
# endif
|
|
|
|
|
|
|
|
// now we do the blocking processing
|
|
|
|
if (m_node.isEmpty())
|
|
|
|
m_node = "*";
|
|
|
|
|
|
|
|
addrinfo *result;
|
|
|
|
int res = getaddrinfo(m_node, m_serv, &hint, &result);
|
|
|
|
// kdDebug(179) << k_funcinfo << "getaddrinfo(\""
|
|
|
|
// << m_node << "\", \"" << m_serv << "\", af="
|
|
|
|
// << m_af << ") returned " << res << endl;
|
|
|
|
|
|
|
|
if (res != 0)
|
|
|
|
{
|
|
|
|
if (checkResolver())
|
|
|
|
{
|
|
|
|
// resolver requires reinitialisation
|
|
|
|
resLock.openClose();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (res)
|
|
|
|
{
|
|
|
|
case EAI_BADFLAGS:
|
|
|
|
results.setError(KResolver::BadFlags);
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef EAI_NODATA
|
|
|
|
// In some systems, EAI_NODATA was #define'd to EAI_NONAME which would break this case.
|
|
|
|
#if EAI_NODATA != EAI_NONAME
|
|
|
|
case EAI_NODATA: // it was removed in RFC 3493
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
case EAI_NONAME:
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EAI_AGAIN:
|
|
|
|
results.setError(KResolver::TryAgain);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EAI_FAIL:
|
|
|
|
results.setError(KResolver::NonRecoverable);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EAI_FAMILY:
|
|
|
|
results.setError(KResolver::UnsupportedFamily);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EAI_SOCKTYPE:
|
|
|
|
results.setError(KResolver::UnsupportedSocketType);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EAI_SERVICE:
|
|
|
|
results.setError(KResolver::UnsupportedService);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EAI_MEMORY:
|
|
|
|
results.setError(KResolver::Memory);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EAI_SYSTEM:
|
|
|
|
results.setError(KResolver::SystemError, errno);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
results.setError(KResolver::UnknownError, errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
finished();
|
|
|
|
return false; // failed
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we are here, lookup succeeded
|
|
|
|
TQString canon;
|
|
|
|
const char *previous_canon = 0L;
|
|
|
|
|
|
|
|
for (addrinfo* p = result; p; p = p->ai_next)
|
|
|
|
{
|
|
|
|
// cache the last canon name to avoid doing the ToUnicode processing unnecessarily
|
|
|
|
if ((previous_canon && !p->ai_canonname) ||
|
|
|
|
(!previous_canon && p->ai_canonname) ||
|
|
|
|
(p->ai_canonname != previous_canon &&
|
|
|
|
strcmp(p->ai_canonname, previous_canon) != 0))
|
|
|
|
{
|
|
|
|
canon = KResolver::domainToUnicode(TQString::fromAscii(p->ai_canonname));
|
|
|
|
previous_canon = p->ai_canonname;
|
|
|
|
}
|
|
|
|
|
|
|
|
results.append(KResolverEntry(p->ai_addr, p->ai_addrlen, p->ai_socktype,
|
|
|
|
p->ai_protocol, canon, m_node));
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(result);
|
|
|
|
results.setError(KResolver::NoError);
|
|
|
|
finished();
|
|
|
|
return results.error() == KResolver::NoError;
|
|
|
|
}
|
|
|
|
while (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // HAVE_GETADDRINFO
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
bool KStandardWorker::sanityCheck()
|
|
|
|
{
|
|
|
|
// check that the requested values are sensible
|
|
|
|
|
|
|
|
if (!nodeName().isEmpty())
|
|
|
|
{
|
|
|
|
TQString node = nodeName();
|
|
|
|
if (node.tqfind('%') != -1)
|
|
|
|
node.truncate(node.tqfind('%'));
|
|
|
|
|
|
|
|
if (node.isEmpty() || node == TQString::tqfromLatin1("*") ||
|
|
|
|
node == TQString::tqfromLatin1("localhost"))
|
|
|
|
m_encodedName.truncate(0);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_encodedName = KResolver::domainToAscii(node);
|
|
|
|
|
|
|
|
if (m_encodedName.isNull())
|
|
|
|
{
|
|
|
|
qDebug("could not encode hostname '%s' (UTF-8)", node.utf8().data());
|
|
|
|
setError(KResolver::NoName);
|
|
|
|
return false; // invalid hostname!
|
|
|
|
}
|
|
|
|
|
|
|
|
// qDebug("Using encoded hostname '%s' for '%s' (UTF-8)", m_encodedName.data(),
|
|
|
|
// node.utf8().data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_encodedName.truncate(0); // just to be sure, but it should be clear already
|
|
|
|
|
|
|
|
if (protocol() == -1)
|
|
|
|
{
|
|
|
|
setError(KResolver::NonRecoverable);
|
|
|
|
return false; // user passed invalid protocol name
|
|
|
|
}
|
|
|
|
|
|
|
|
return true; // it's sane
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KStandardWorker::resolveScopeId()
|
|
|
|
{
|
|
|
|
// we must test the original name, not the encoded one
|
|
|
|
scopeid = 0;
|
|
|
|
int pos = nodeName().tqfindRev('%');
|
|
|
|
if (pos == -1)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
TQString scopename = nodeName().mid(pos + 1);
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
scopeid = scopename.toInt(&ok);
|
|
|
|
if (!ok)
|
|
|
|
{
|
|
|
|
// it's not a number
|
|
|
|
// therefore, it's an interface name
|
|
|
|
#ifdef HAVE_IF_NAMETOINDEX
|
|
|
|
scopeid = if_nametoindex(scopename.latin1());
|
|
|
|
#else
|
|
|
|
scopeid = 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KStandardWorker::resolveService()
|
|
|
|
{
|
|
|
|
// find the service first
|
|
|
|
bool ok;
|
|
|
|
port = serviceName().toUInt(&ok);
|
|
|
|
if (!ok)
|
|
|
|
{
|
|
|
|
// service name does not contain a port number
|
|
|
|
// must be a name
|
|
|
|
|
|
|
|
if (serviceName().isEmpty() || serviceName().compare(TQString::tqfromLatin1("*")) == 0)
|
|
|
|
port = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// it's a name. We need the protocol name in order to lookup.
|
|
|
|
TQCString protoname = protocolName();
|
|
|
|
|
|
|
|
if (protoname.isEmpty() && protocol())
|
|
|
|
{
|
|
|
|
protoname = KResolver::protocolName(protocol()).first();
|
|
|
|
|
|
|
|
// if it's still empty...
|
|
|
|
if (protoname.isEmpty())
|
|
|
|
{
|
|
|
|
// lookup failed!
|
|
|
|
setError(KResolver::NoName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
protoname = "tcp";
|
|
|
|
|
|
|
|
// it's not, so we can do a port lookup
|
|
|
|
int result = KResolver::servicePort(serviceName().latin1(), protoname);
|
|
|
|
if (result == -1)
|
|
|
|
{
|
|
|
|
// lookup failed!
|
|
|
|
setError(KResolver::NoName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// it worked, we have a port number
|
|
|
|
port = (TQ_UINT16)result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// we found a port
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KResolver::ErrorCodes KStandardWorker::addUnix()
|
|
|
|
{
|
|
|
|
// before trying to add, see if the user wants Unix sockets
|
|
|
|
if ((familyMask() & KResolver::UnixFamily) == 0)
|
|
|
|
// no, Unix sockets are not wanted
|
|
|
|
return KResolver::UnsupportedFamily;
|
|
|
|
|
|
|
|
// now check if the requested data are good for a Unix socket
|
|
|
|
if (!m_encodedName.isEmpty())
|
|
|
|
return KResolver::AddrFamily; // non local hostname
|
|
|
|
|
|
|
|
if (protocol() || static_cast<const char *>(protocolName()))
|
|
|
|
return KResolver::BadFlags; // cannot have Unix sockets with protocols
|
|
|
|
|
|
|
|
TQString pathname = serviceName();
|
|
|
|
if (pathname.isEmpty())
|
|
|
|
return KResolver::NoName;; // no path?
|
|
|
|
|
|
|
|
if (pathname[0] != '/')
|
|
|
|
// non absolute pathname
|
|
|
|
// put it in /tmp
|
|
|
|
pathname.prepend("/tmp/");
|
|
|
|
|
|
|
|
// qDebug("QNoResolveWorker::addUnix(): adding Unix socket for %s", pathname.local8Bit().data());
|
|
|
|
KUnixSocketAddress sa(pathname);
|
|
|
|
int socktype = socketType();
|
|
|
|
if (socktype == 0)
|
|
|
|
socktype = SOCK_STREAM; // default
|
|
|
|
|
|
|
|
results.append(KResolverEntry(sa, socktype, 0));
|
|
|
|
setError(KResolver::NoError);
|
|
|
|
|
|
|
|
return KResolver::NoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KStandardWorker::resolveNumerically()
|
|
|
|
{
|
|
|
|
// if the NoResolve flag is active, our result from this point forward
|
|
|
|
// will always be true, even if the resolution failed.
|
|
|
|
// that indicates that our result is authoritative.
|
|
|
|
|
|
|
|
bool wantV4 = familyMask() & KResolver::IPv4Family,
|
|
|
|
wantV6 = familyMask() & KResolver::IPv6Family;
|
|
|
|
|
|
|
|
if (!wantV6 && !wantV4)
|
|
|
|
// no Internet address is wanted!
|
|
|
|
return (flags() & KResolver::NoResolve);
|
|
|
|
|
|
|
|
// now try to find results
|
|
|
|
if (!resolveScopeId() || !resolveService())
|
|
|
|
return (flags() & KResolver::NoResolve);
|
|
|
|
|
|
|
|
// we have scope IDs and port numbers
|
|
|
|
// now try to resolve the hostname numerically
|
|
|
|
KInetSocketAddress sa;
|
|
|
|
setError(KResolver::NoError);
|
|
|
|
sa.setHost(KIpAddress(TQString::tqfromLatin1(m_encodedName)));
|
|
|
|
|
|
|
|
// if it failed, the length was reset to 0
|
|
|
|
bool ok = sa.length() != 0;
|
|
|
|
|
|
|
|
sa.setPort(port);
|
|
|
|
if (sa.ipVersion() == 6)
|
|
|
|
sa.setScopeId(scopeid);
|
|
|
|
int proto = protocol();
|
|
|
|
int socktype = socketType();
|
|
|
|
if (socktype == 0)
|
|
|
|
socktype = SOCK_STREAM;
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
{
|
|
|
|
// the given hostname was successfully converted to an IP address
|
|
|
|
// check if the user wanted this kind of address
|
|
|
|
|
|
|
|
if ((sa.ipVersion() == 4 && wantV4) ||
|
|
|
|
(sa.ipVersion() == 6 && wantV6))
|
|
|
|
results.append(KResolverEntry(sa, socktype, proto));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Note: the address *IS* a numeric IP
|
|
|
|
// but it's not of the kind the user asked for
|
|
|
|
//
|
|
|
|
// that means that it cannot be a Unix socket (because it's an IP)
|
|
|
|
// and that means that no resolution will tell us otherwise
|
|
|
|
//
|
|
|
|
// This is a failed resolution
|
|
|
|
|
|
|
|
setError(KResolver::AddrFamily);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (m_encodedName.isEmpty())
|
|
|
|
{
|
|
|
|
// user wanted localhost
|
|
|
|
if (flags() & KResolver::Passive)
|
|
|
|
{
|
|
|
|
if (wantV6)
|
|
|
|
{
|
|
|
|
sa.setHost(KIpAddress::anyhostV6);
|
|
|
|
results.append(KResolverEntry(sa, socktype, proto));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wantV4)
|
|
|
|
{
|
|
|
|
sa.setHost(KIpAddress::anyhostV4);
|
|
|
|
results.append(KResolverEntry(sa, socktype, proto));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (wantV6)
|
|
|
|
{
|
|
|
|
sa.setHost(KIpAddress::localhostV6);
|
|
|
|
results.append(KResolverEntry(sa, socktype, proto));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wantV4)
|
|
|
|
{
|
|
|
|
sa.setHost(KIpAddress::localhostV4);
|
|
|
|
results.append(KResolverEntry(sa, socktype, proto));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ok = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// probably bad flags, since the address is not convertible without
|
|
|
|
// resolution
|
|
|
|
|
|
|
|
setError(KResolver::BadFlags);
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ok || (flags() & KResolver::NoResolve);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KStandardWorker::preprocess()
|
|
|
|
{
|
|
|
|
// check sanity
|
|
|
|
if (!sanityCheck())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// this worker class can only handle known families
|
|
|
|
if (familyMask() & KResolver::UnknownFamily)
|
|
|
|
{
|
|
|
|
setError(KResolver::UnsupportedFamily);
|
|
|
|
return false; // we don't know about this
|
|
|
|
}
|
|
|
|
|
|
|
|
// check the socket types
|
|
|
|
if (socketType() != SOCK_STREAM && socketType() != SOCK_DGRAM && socketType() != 0)
|
|
|
|
{
|
|
|
|
setError(KResolver::UnsupportedSocketType);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we can resolve all numerically
|
|
|
|
// resolveNumerically always returns true if the NoResolve flag is set
|
|
|
|
if (resolveNumerically() || m_encodedName.isEmpty())
|
|
|
|
{
|
|
|
|
// indeed, we have resolved numerically
|
|
|
|
setError(addUnix());
|
|
|
|
if (results.count())
|
|
|
|
setError(KResolver::NoError);
|
|
|
|
finished();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the user wants something we know about
|
|
|
|
#ifdef AF_INET6
|
|
|
|
# define mask (KResolver::IPv6Family | KResolver::IPv4Family | KResolver::UnixFamily)
|
|
|
|
#else
|
|
|
|
# define mask (KResolver::IPv4Family | KResolver::UnixFamily)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((familyMask() & mask) == 0)
|
|
|
|
// errr... nothing we know about
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#undef mask
|
|
|
|
|
|
|
|
return true; // it's ok
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KStandardWorker::run()
|
|
|
|
{
|
|
|
|
#ifndef HAVE_GETADDRINFO
|
|
|
|
// check the scope id first
|
|
|
|
// since most of the resolutions won't have a scope id, this should be fast
|
|
|
|
// and we won't have wasted time on services if this fails
|
|
|
|
if (!resolveScopeId())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// resolve the service now, before entering the blocking operation
|
|
|
|
if (!resolveService())
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// good
|
|
|
|
// now we need the hostname
|
|
|
|
setError(KResolver::NoName);
|
|
|
|
|
|
|
|
// these are the family types that we know of
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
KResolver::SocketFamilies mask;
|
|
|
|
int af;
|
|
|
|
} families[] = { { KResolver::IPv4Family, AF_INET }
|
|
|
|
#ifdef AF_INET6
|
|
|
|
, { KResolver::IPv6Family, AF_INET6 }
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
int familyCount = sizeof(families)/sizeof(families[0]);
|
|
|
|
bool skipIPv6 = !hasIPv6();
|
|
|
|
resultList.setAutoDelete(true);
|
|
|
|
|
|
|
|
for (int i = 0; i < familyCount; i++)
|
|
|
|
if (familyMask() & families[i].mask)
|
|
|
|
{
|
|
|
|
#ifdef AF_INET6
|
|
|
|
if (skipIPv6 && families[i].af == AF_INET6)
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
KResolverWorkerBase *worker;
|
|
|
|
KResolverResults *res = new KResolverResults;
|
|
|
|
resultList.append(res);
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
|
|
worker = new GetAddrInfoThread(m_encodedName,
|
|
|
|
serviceName().latin1(),
|
|
|
|
families[i].af, flags(), res);
|
|
|
|
#else
|
|
|
|
worker = new GetHostByNameThread(m_encodedName, port, scopeid,
|
|
|
|
families[i].af, res);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
enqueue(worker);
|
|
|
|
}
|
|
|
|
|
|
|
|
// not finished
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KStandardWorker::postprocess()
|
|
|
|
{
|
|
|
|
if (results.count())
|
|
|
|
return true; // no need
|
|
|
|
// now copy over what we need from the underlying results
|
|
|
|
|
|
|
|
// start backwards because IPv6 was launched later (if at all)
|
|
|
|
if (resultList.isEmpty())
|
|
|
|
{
|
|
|
|
results.setError(KResolver::NoName);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KResolverResults *rr = resultList.last();
|
|
|
|
while (rr)
|
|
|
|
{
|
|
|
|
if (!rr->isEmpty())
|
|
|
|
{
|
|
|
|
results.setError(KResolver::NoError);
|
|
|
|
KResolverResults::Iterator it = rr->begin();
|
|
|
|
for ( ; it != rr->end(); ++it)
|
|
|
|
results.append(*it);
|
|
|
|
}
|
|
|
|
else if (results.isEmpty())
|
|
|
|
// this generated an error
|
|
|
|
// copy the error code over
|
|
|
|
setError(rr->error(), rr->systemError());
|
|
|
|
|
|
|
|
rr = resultList.prev();
|
|
|
|
}
|
|
|
|
|
|
|
|
resultList.clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
|
|
KGetAddrinfoWorker::~KGetAddrinfoWorker()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGetAddrinfoWorker::preprocess()
|
|
|
|
{
|
|
|
|
// getaddrinfo(3) can always handle any kind of request that makes sense
|
|
|
|
if (!sanityCheck())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (flags() & KResolver::NoResolve)
|
|
|
|
// oops, numeric resolution?
|
|
|
|
return run();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGetAddrinfoWorker::run()
|
|
|
|
{
|
|
|
|
// make an AF_UNSPEC getaddrinfo(3) call
|
|
|
|
GetAddrInfoThread worker(m_encodedName, serviceName().latin1(),
|
|
|
|
AF_UNSPEC, flags(), &results);
|
|
|
|
|
|
|
|
if (!worker.run())
|
|
|
|
{
|
|
|
|
if (wantThis(AF_UNIX))
|
|
|
|
{
|
|
|
|
if (addUnix() == KResolver::NoError)
|
|
|
|
setError(KResolver::NoError);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
setError(worker.results.error(), worker.results.systemError());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The worker has finished working
|
|
|
|
// now copy over only what we may want
|
|
|
|
// keep track of any Unix-domain sockets
|
|
|
|
|
|
|
|
bool seen_unix = false;
|
|
|
|
KResolverResults::Iterator it = results.begin();
|
|
|
|
for ( ; it != results.end(); )
|
|
|
|
{
|
|
|
|
if ((*it).family() == AF_UNIX)
|
|
|
|
seen_unix = true;
|
|
|
|
if (!wantThis((*it).family()))
|
|
|
|
it = results.remove(it);
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!seen_unix)
|
|
|
|
addUnix();
|
|
|
|
|
|
|
|
finished();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KGetAddrinfoWorker::wantThis(int family)
|
|
|
|
{
|
|
|
|
// tells us if the user wants a socket of this family
|
|
|
|
|
|
|
|
#ifdef AF_INET6
|
|
|
|
if (family == AF_INET6 && familyMask() & KResolver::IPv6Family)
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
if (family == AF_INET && familyMask() & KResolver::IPv4Family)
|
|
|
|
return true;
|
|
|
|
if (family == AF_UNIX && familyMask() & KResolver::UnixFamily)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// it's not a family we know about...
|
|
|
|
if (familyMask() & KResolver::UnknownFamily)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void KNetwork::Internal::initStandardWorkers()
|
|
|
|
{
|
|
|
|
//KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KBlacklistWorker>);
|
|
|
|
KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KStandardWorker>);
|
|
|
|
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
|
|
KResolverWorkerFactoryBase::registerNewWorker(new KResolverWorkerFactory<KGetAddrinfoWorker>);
|
|
|
|
#endif
|
|
|
|
}
|