You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1165 lines
26 KiB
1165 lines
26 KiB
15 years ago
|
/* -*- C++ -*-
|
||
|
* Copyright (C) 2003-2005 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"
|
||
|
|
||
|
// System includes
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <sys/param.h>
|
||
|
#include <errno.h>
|
||
|
#include <netdb.h>
|
||
|
#include <time.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
// Qt includes
|
||
|
#include <qapplication.h>
|
||
|
#include <qstring.h>
|
||
|
#include <qcstring.h>
|
||
|
#include <qstrlist.h>
|
||
|
#include <qstringlist.h>
|
||
|
#include <qshared.h>
|
||
|
#include <qdatetime.h>
|
||
|
#include <qtimer.h>
|
||
|
#include <qmutex.h>
|
||
|
#include <qguardedptr.h>
|
||
|
|
||
|
// IDN
|
||
|
#ifdef HAVE_IDNA_H
|
||
|
# include <idna.h>
|
||
|
#endif
|
||
|
|
||
|
// KDE
|
||
|
#include <klocale.h>
|
||
|
|
||
|
// Us
|
||
|
#include "kresolver.h"
|
||
|
#include "kresolver_p.h"
|
||
|
#include "ksocketaddress.h"
|
||
|
|
||
|
#ifdef NEED_MUTEX
|
||
|
#warning "mutex"
|
||
|
QMutex getXXbyYYmutex;
|
||
|
#endif
|
||
|
|
||
|
using namespace KNetwork;
|
||
|
using namespace KNetwork::Internal;
|
||
|
|
||
|
/////////////////////////////////////////////
|
||
|
// class KResolverEntry
|
||
|
|
||
|
class KNetwork::KResolverEntryPrivate: public QShared
|
||
|
{
|
||
|
public:
|
||
|
KSocketAddress addr;
|
||
|
int socktype;
|
||
|
int protocol;
|
||
|
QString canonName;
|
||
|
QCString encodedName;
|
||
|
|
||
|
inline KResolverEntryPrivate() :
|
||
|
socktype(0), protocol(0)
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
// default constructor
|
||
|
KResolverEntry::KResolverEntry() :
|
||
|
d(0L)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// constructor with stuff
|
||
|
KResolverEntry::KResolverEntry(const KSocketAddress& addr, int socktype, int protocol,
|
||
|
const QString& canonName, const QCString& encodedName) :
|
||
|
d(new KResolverEntryPrivate)
|
||
|
{
|
||
|
d->addr = addr;
|
||
|
d->socktype = socktype;
|
||
|
d->protocol = protocol;
|
||
|
d->canonName = canonName;
|
||
|
d->encodedName = encodedName;
|
||
|
}
|
||
|
|
||
|
// constructor with even more stuff
|
||
|
KResolverEntry::KResolverEntry(const struct sockaddr* sa, Q_UINT16 salen, int socktype,
|
||
|
int protocol, const QString& canonName,
|
||
|
const QCString& encodedName) :
|
||
|
d(new KResolverEntryPrivate)
|
||
|
{
|
||
|
d->addr = KSocketAddress(sa, salen);
|
||
|
d->socktype = socktype;
|
||
|
d->protocol = protocol;
|
||
|
d->canonName = canonName;
|
||
|
d->encodedName = encodedName;
|
||
|
}
|
||
|
|
||
|
// copy constructor
|
||
|
KResolverEntry::KResolverEntry(const KResolverEntry& that) :
|
||
|
d(0L)
|
||
|
{
|
||
|
*this = that;
|
||
|
}
|
||
|
|
||
|
// destructor
|
||
|
KResolverEntry::~KResolverEntry()
|
||
|
{
|
||
|
if (d == 0L)
|
||
|
return;
|
||
|
|
||
|
if (d->deref())
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
// returns the socket address
|
||
|
KSocketAddress KResolverEntry::address() const
|
||
|
{
|
||
|
return d ? d->addr : KSocketAddress();
|
||
|
}
|
||
|
|
||
|
// returns the length
|
||
|
Q_UINT16 KResolverEntry::length() const
|
||
|
{
|
||
|
return d ? d->addr.length() : 0;
|
||
|
}
|
||
|
|
||
|
// returns the family
|
||
|
int KResolverEntry::family() const
|
||
|
{
|
||
|
return d ? d->addr.family() : AF_UNSPEC;
|
||
|
}
|
||
|
|
||
|
// returns the canonical name
|
||
|
QString KResolverEntry::canonicalName() const
|
||
|
{
|
||
|
return d ? d->canonName : QString::null;
|
||
|
}
|
||
|
|
||
|
// returns the encoded name
|
||
|
QCString KResolverEntry::encodedName() const
|
||
|
{
|
||
|
return d ? d->encodedName : QCString();
|
||
|
}
|
||
|
|
||
|
// returns the socket type
|
||
|
int KResolverEntry::socketType() const
|
||
|
{
|
||
|
return d ? d->socktype : 0;
|
||
|
}
|
||
|
|
||
|
// returns the protocol
|
||
|
int KResolverEntry::protocol() const
|
||
|
{
|
||
|
return d ? d->protocol : 0;
|
||
|
}
|
||
|
|
||
|
// assignment operator
|
||
|
KResolverEntry& KResolverEntry::operator= (const KResolverEntry& that)
|
||
|
{
|
||
|
// copy the data
|
||
|
if (that.d)
|
||
|
that.d->ref();
|
||
|
|
||
|
if (d && d->deref())
|
||
|
delete d;
|
||
|
|
||
|
d = that.d;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////
|
||
|
// class KResolverResults
|
||
|
|
||
|
class KNetwork::KResolverResultsPrivate
|
||
|
{
|
||
|
public:
|
||
|
QString node, service;
|
||
|
int errorcode, syserror;
|
||
|
|
||
|
KResolverResultsPrivate() :
|
||
|
errorcode(0), syserror(0)
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
// default constructor
|
||
|
KResolverResults::KResolverResults()
|
||
|
: d(new KResolverResultsPrivate)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// copy constructor
|
||
|
KResolverResults::KResolverResults(const KResolverResults& other)
|
||
|
: QValueList<KResolverEntry>(other), d(new KResolverResultsPrivate)
|
||
|
{
|
||
|
*d = *other.d;
|
||
|
}
|
||
|
|
||
|
// destructor
|
||
|
KResolverResults::~KResolverResults()
|
||
|
{
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
// assignment operator
|
||
|
KResolverResults&
|
||
|
KResolverResults::operator= (const KResolverResults& other)
|
||
|
{
|
||
|
if (this == &other)
|
||
|
return *this;
|
||
|
|
||
|
// copy over the other data
|
||
|
*d = *other.d;
|
||
|
|
||
|
// now let QValueList do the rest of the work
|
||
|
QValueList<KResolverEntry>::operator =(other);
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// gets the error code
|
||
|
int KResolverResults::error() const
|
||
|
{
|
||
|
return d->errorcode;
|
||
|
}
|
||
|
|
||
|
// gets the system errno
|
||
|
int KResolverResults::systemError() const
|
||
|
{
|
||
|
return d->syserror;
|
||
|
}
|
||
|
|
||
|
// sets the error codes
|
||
|
void KResolverResults::setError(int errorcode, int systemerror)
|
||
|
{
|
||
|
d->errorcode = errorcode;
|
||
|
d->syserror = systemerror;
|
||
|
}
|
||
|
|
||
|
// gets the hostname
|
||
|
QString KResolverResults::nodeName() const
|
||
|
{
|
||
|
return d->node;
|
||
|
}
|
||
|
|
||
|
// gets the service name
|
||
|
QString KResolverResults::serviceName() const
|
||
|
{
|
||
|
return d->service;
|
||
|
}
|
||
|
|
||
|
// sets the address
|
||
|
void KResolverResults::setAddress(const QString& node,
|
||
|
const QString& service)
|
||
|
{
|
||
|
d->node = node;
|
||
|
d->service = service;
|
||
|
}
|
||
|
|
||
|
void KResolverResults::virtual_hook( int, void* )
|
||
|
{ /*BASE::virtual_hook( id, data );*/ }
|
||
|
|
||
|
|
||
|
///////////////////////
|
||
|
// class KResolver
|
||
|
|
||
|
QStringList *KResolver::idnDomains = 0;
|
||
|
|
||
|
|
||
|
// default constructor
|
||
|
KResolver::KResolver(QObject *parent, const char *name)
|
||
|
: QObject(parent, name), d(new KResolverPrivate(this))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// constructor with host and service
|
||
|
KResolver::KResolver(const QString& nodename, const QString& servicename,
|
||
|
QObject *parent, const char *name)
|
||
|
: QObject(parent, name), d(new KResolverPrivate(this, nodename, servicename))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// destructor
|
||
|
KResolver::~KResolver()
|
||
|
{
|
||
|
cancel(false);
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
// get the status
|
||
|
int KResolver::status() const
|
||
|
{
|
||
|
return d->status;
|
||
|
}
|
||
|
|
||
|
// get the error code
|
||
|
int KResolver::error() const
|
||
|
{
|
||
|
return d->errorcode;
|
||
|
}
|
||
|
|
||
|
// get the errno
|
||
|
int KResolver::systemError() const
|
||
|
{
|
||
|
return d->syserror;
|
||
|
}
|
||
|
|
||
|
// are we running?
|
||
|
bool KResolver::isRunning() const
|
||
|
{
|
||
|
return d->status > 0 && d->status < Success;
|
||
|
}
|
||
|
|
||
|
// get the hostname
|
||
|
QString KResolver::nodeName() const
|
||
|
{
|
||
|
return d->input.node;
|
||
|
}
|
||
|
|
||
|
// get the service
|
||
|
QString KResolver::serviceName() const
|
||
|
{
|
||
|
return d->input.service;
|
||
|
}
|
||
|
|
||
|
// sets the hostname
|
||
|
void KResolver::setNodeName(const QString& nodename)
|
||
|
{
|
||
|
// don't touch those values if we're working!
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
d->input.node = nodename;
|
||
|
d->status = Idle;
|
||
|
d->results.setAddress(nodename, d->input.service);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// sets the service
|
||
|
void KResolver::setServiceName(const QString& service)
|
||
|
{
|
||
|
// don't change if running
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
d->input.service = service;
|
||
|
d->status = Idle;
|
||
|
d->results.setAddress(d->input.node, service);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// sets the address
|
||
|
void KResolver::setAddress(const QString& nodename, const QString& service)
|
||
|
{
|
||
|
setNodeName(nodename);
|
||
|
setServiceName(service);
|
||
|
}
|
||
|
|
||
|
// get the flags
|
||
|
int KResolver::flags() const
|
||
|
{
|
||
|
return d->input.flags;
|
||
|
}
|
||
|
|
||
|
// sets the flags
|
||
|
int KResolver::setFlags(int flags)
|
||
|
{
|
||
|
int oldflags = d->input.flags;
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
d->input.flags = flags;
|
||
|
d->status = Idle;
|
||
|
}
|
||
|
return oldflags;
|
||
|
}
|
||
|
|
||
|
// sets the family mask
|
||
|
void KResolver::setFamily(int families)
|
||
|
{
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
d->input.familyMask = families;
|
||
|
d->status = Idle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// sets the socket type
|
||
|
void KResolver::setSocketType(int type)
|
||
|
{
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
d->input.socktype = type;
|
||
|
d->status = Idle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// sets the protocol
|
||
|
void KResolver::setProtocol(int protonum, const char *name)
|
||
|
{
|
||
|
if (isRunning())
|
||
|
return; // can't change now
|
||
|
|
||
|
// we copy the given protocol name. If it isn't an empty string
|
||
|
// and the protocol number was 0, we will look it up in /etc/protocols
|
||
|
// we also leave the error reporting to the actual lookup routines, in
|
||
|
// case the given protocol name doesn't exist
|
||
|
|
||
|
d->input.protocolName = name;
|
||
|
if (protonum == 0 && name != 0L && *name != '\0')
|
||
|
{
|
||
|
// must look up the protocol number
|
||
|
d->input.protocol = KResolver::protocolNumber(name);
|
||
|
}
|
||
|
else
|
||
|
d->input.protocol = protonum;
|
||
|
d->status = Idle;
|
||
|
}
|
||
|
|
||
|
bool KResolver::start()
|
||
|
{
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
d->results.empty();
|
||
|
|
||
|
// is there anything to be queued?
|
||
|
if (d->input.node.isEmpty() && d->input.service.isEmpty())
|
||
|
{
|
||
|
d->status = KResolver::Success;
|
||
|
emitFinished();
|
||
|
}
|
||
|
else
|
||
|
KResolverManager::manager()->enqueue(this, 0L);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool KResolver::wait(int msec)
|
||
|
{
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
emitFinished();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QMutexLocker locker(&d->mutex);
|
||
|
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
// it was running and no longer is?
|
||
|
// That means the manager has finished its processing and has posted
|
||
|
// an event for the signal to be emitted already. This means the signal
|
||
|
// will be emitted twice!
|
||
|
|
||
|
emitFinished();
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
QTime t;
|
||
|
t.start();
|
||
|
|
||
|
while (!msec || t.elapsed() < msec)
|
||
|
{
|
||
|
// wait on the manager to broadcast completion
|
||
|
d->waiting = true;
|
||
|
if (msec)
|
||
|
KResolverManager::manager()->notifyWaiters.wait(&d->mutex, msec - t.elapsed());
|
||
|
else
|
||
|
KResolverManager::manager()->notifyWaiters.wait(&d->mutex);
|
||
|
|
||
|
// the manager has processed
|
||
|
// see if this object is done
|
||
|
if (!isRunning())
|
||
|
{
|
||
|
// it's done
|
||
|
d->waiting = false;
|
||
|
emitFinished();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if we've got here, we've timed out
|
||
|
d->waiting = false;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void KResolver::cancel(bool emitSignal)
|
||
|
{
|
||
|
KResolverManager::manager()->dequeue(this);
|
||
|
if (emitSignal)
|
||
|
emitFinished();
|
||
|
}
|
||
|
|
||
|
KResolverResults
|
||
|
KResolver::results() const
|
||
|
{
|
||
|
if (!isRunning())
|
||
|
return d->results;
|
||
|
|
||
|
// return a dummy, empty result
|
||
|
KResolverResults r;
|
||
|
r.setAddress(d->input.node, d->input.service);
|
||
|
r.setError(d->errorcode, d->syserror);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
bool KResolver::event(QEvent* e)
|
||
|
{
|
||
|
if (static_cast<int>(e->type()) == KResolverManager::ResolutionCompleted)
|
||
|
{
|
||
|
emitFinished();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void KResolver::emitFinished()
|
||
|
{
|
||
|
if (isRunning())
|
||
|
d->status = KResolver::Success;
|
||
|
|
||
|
QGuardedPtr<QObject> p = this; // guard against deletion
|
||
|
|
||
|
emit finished(d->results);
|
||
|
|
||
|
if (p && d->deleteWhenDone)
|
||
|
deleteLater(); // in QObject
|
||
|
}
|
||
|
|
||
|
QString KResolver::errorString(int errorcode, int syserror)
|
||
|
{
|
||
|
// no i18n now...
|
||
|
static const char * const messages[] =
|
||
|
{
|
||
|
I18N_NOOP("no error"), // NoError
|
||
|
I18N_NOOP("requested family not supported for this host name"), // AddrFamily
|
||
|
I18N_NOOP("temporary failure in name resolution"), // TryAgain
|
||
|
I18N_NOOP("non-recoverable failure in name resolution"), // NonRecoverable
|
||
|
I18N_NOOP("invalid flags"), // BadFlags
|
||
|
I18N_NOOP("memory allocation failure"), // Memory
|
||
|
I18N_NOOP("name or service not known"), // NoName
|
||
|
I18N_NOOP("requested family not supported"), // UnsupportedFamily
|
||
|
I18N_NOOP("requested service not supported for this socket type"), // UnsupportedService
|
||
|
I18N_NOOP("requested socket type not supported"), // UnsupportedSocketType
|
||
|
I18N_NOOP("unknown error"), // UnknownError
|
||
|
I18N_NOOP2("1: the i18n'ed system error code, from errno",
|
||
|
"system error: %1") // SystemError
|
||
|
};
|
||
|
|
||
|
// handle the special value
|
||
|
if (errorcode == Canceled)
|
||
|
return i18n("request was canceled");
|
||
|
|
||
|
if (errorcode > 0 || errorcode < SystemError)
|
||
|
return QString::null;
|
||
|
|
||
|
QString msg = i18n(messages[-errorcode]);
|
||
|
if (errorcode == SystemError)
|
||
|
msg.arg(QString::fromLocal8Bit(strerror(syserror)));
|
||
|
|
||
|
return msg;
|
||
|
}
|
||
|
|
||
|
KResolverResults
|
||
|
KResolver::resolve(const QString& host, const QString& service, int flags,
|
||
|
int families)
|
||
|
{
|
||
|
KResolver qres(host, service, qApp, "synchronous KResolver");
|
||
|
qres.setFlags(flags);
|
||
|
qres.setFamily(families);
|
||
|
qres.start();
|
||
|
qres.wait();
|
||
|
return qres.results();
|
||
|
}
|
||
|
|
||
|
bool KResolver::resolveAsync(QObject* userObj, const char *userSlot,
|
||
|
const QString& host, const QString& service,
|
||
|
int flags, int families)
|
||
|
{
|
||
|
KResolver* qres = new KResolver(host, service, qApp, "asynchronous KResolver");
|
||
|
QObject::connect(qres, SIGNAL(finished(KResolverResults)), userObj, userSlot);
|
||
|
qres->setFlags(flags);
|
||
|
qres->setFamily(families);
|
||
|
qres->d->deleteWhenDone = true; // this is the only difference from the example code
|
||
|
return qres->start();
|
||
|
}
|
||
|
|
||
|
QStrList KResolver::protocolName(int protonum)
|
||
|
{
|
||
|
struct protoent *pe = 0L;
|
||
|
#ifndef HAVE_GETPROTOBYNAME_R
|
||
|
QMutexLocker locker(&getXXbyYYmutex);
|
||
|
|
||
|
pe = getprotobynumber(protonum);
|
||
|
|
||
|
#else
|
||
|
size_t buflen = 1024;
|
||
|
struct protoent protobuf;
|
||
|
char *buf;
|
||
|
do
|
||
|
{
|
||
|
buf = new char[buflen];
|
||
|
# ifdef USE_SOLARIS // Solaris uses a 4 argument getprotobynumber_r which returns struct *protoent or NULL
|
||
|
if ((pe = getprotobynumber_r(protonum, &protobuf, buf, buflen)) && (errno == ERANGE))
|
||
|
# else
|
||
|
if (getprotobynumber_r(protonum, &protobuf, buf, buflen, &pe) == ERANGE)
|
||
|
# endif
|
||
|
{
|
||
|
pe = 0L;
|
||
|
buflen += 1024;
|
||
|
delete [] buf;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
while (pe == 0L);
|
||
|
#endif
|
||
|
|
||
|
// Do common processing
|
||
|
QStrList lst(true); // use deep copies
|
||
|
if (pe != NULL)
|
||
|
{
|
||
|
lst.append(pe->p_name);
|
||
|
for (char **p = pe->p_aliases; *p; p++)
|
||
|
lst.append(*p);
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_GETPROTOBYNAME_R
|
||
|
delete [] buf;
|
||
|
#endif
|
||
|
|
||
|
return lst;
|
||
|
}
|
||
|
|
||
|
QStrList KResolver::protocolName(const char *protoname)
|
||
|
{
|
||
|
struct protoent *pe = 0L;
|
||
|
#ifndef HAVE_GETPROTOBYNAME_R
|
||
|
QMutexLocker locker(&getXXbyYYmutex);
|
||
|
|
||
|
pe = getprotobyname(protoname);
|
||
|
|
||
|
#else
|
||
|
size_t buflen = 1024;
|
||
|
struct protoent protobuf;
|
||
|
char *buf;
|
||
|
do
|
||
|
{
|
||
|
buf = new char[buflen];
|
||
|
# ifdef USE_SOLARIS // Solaris uses a 4 argument getprotobyname_r which returns struct *protoent or NULL
|
||
|
if ((pe = getprotobyname_r(protoname, &protobuf, buf, buflen)) && (errno == ERANGE))
|
||
|
# else
|
||
|
if (getprotobyname_r(protoname, &protobuf, buf, buflen, &pe) == ERANGE)
|
||
|
# endif
|
||
|
{
|
||
|
pe = 0L;
|
||
|
buflen += 1024;
|
||
|
delete [] buf;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
while (pe == 0L);
|
||
|
#endif
|
||
|
|
||
|
// Do common processing
|
||
|
QStrList lst(true); // use deep copies
|
||
|
if (pe != NULL)
|
||
|
{
|
||
|
lst.append(pe->p_name);
|
||
|
for (char **p = pe->p_aliases; *p; p++)
|
||
|
lst.append(*p);
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_GETPROTOBYNAME_R
|
||
|
delete [] buf;
|
||
|
#endif
|
||
|
|
||
|
return lst;
|
||
|
}
|
||
|
|
||
|
int KResolver::protocolNumber(const char *protoname)
|
||
|
{
|
||
|
struct protoent *pe = 0L;
|
||
|
#ifndef HAVE_GETPROTOBYNAME_R
|
||
|
QMutexLocker locker(&getXXbyYYmutex);
|
||
|
|
||
|
pe = getprotobyname(protoname);
|
||
|
|
||
|
#else
|
||
|
size_t buflen = 1024;
|
||
|
struct protoent protobuf;
|
||
|
char *buf;
|
||
|
do
|
||
|
{
|
||
|
buf = new char[buflen];
|
||
|
# ifdef USE_SOLARIS // Solaris uses a 4 argument getprotobyname_r which returns struct *protoent or NULL
|
||
|
if ((pe = getprotobyname_r(protoname, &protobuf, buf, buflen)) && (errno == ERANGE))
|
||
|
# else
|
||
|
if (getprotobyname_r(protoname, &protobuf, buf, buflen, &pe) == ERANGE)
|
||
|
# endif
|
||
|
{
|
||
|
pe = 0L;
|
||
|
buflen += 1024;
|
||
|
delete [] buf;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
while (pe == 0L);
|
||
|
#endif
|
||
|
|
||
|
// Do common processing
|
||
|
int protonum = -1;
|
||
|
if (pe != NULL)
|
||
|
protonum = pe->p_proto;
|
||
|
|
||
|
#ifdef HAVE_GETPROTOBYNAME_R
|
||
|
delete [] buf;
|
||
|
#endif
|
||
|
|
||
|
return protonum;
|
||
|
}
|
||
|
|
||
|
int KResolver::servicePort(const char *servname, const char *protoname)
|
||
|
{
|
||
|
struct servent *se = 0L;
|
||
|
#ifndef HAVE_GETSERVBYNAME_R
|
||
|
QMutexLocker locker(&getXXbyYYmutex);
|
||
|
|
||
|
se = getservbyname(servname, protoname);
|
||
|
|
||
|
#else
|
||
|
size_t buflen = 1024;
|
||
|
struct servent servbuf;
|
||
|
char *buf;
|
||
|
do
|
||
|
{
|
||
|
buf = new char[buflen];
|
||
|
# ifdef USE_SOLARIS // Solaris uses a 5 argument getservbyname_r which returns struct *servent or NULL
|
||
|
if ((se = getservbyname_r(servname, protoname, &servbuf, buf, buflen)) && (errno == ERANGE))
|
||
|
# else
|
||
|
if (getservbyname_r(servname, protoname, &servbuf, buf, buflen, &se) == ERANGE)
|
||
|
# endif
|
||
|
{
|
||
|
se = 0L;
|
||
|
buflen += 1024;
|
||
|
delete [] buf;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
while (se == 0L);
|
||
|
#endif
|
||
|
|
||
|
// Do common processing
|
||
|
int servport = -1;
|
||
|
if (se != NULL)
|
||
|
servport = ntohs(se->s_port);
|
||
|
|
||
|
#ifdef HAVE_GETSERVBYNAME_R
|
||
|
delete [] buf;
|
||
|
#endif
|
||
|
|
||
|
return servport;
|
||
|
}
|
||
|
|
||
|
QStrList KResolver::serviceName(const char* servname, const char *protoname)
|
||
|
{
|
||
|
struct servent *se = 0L;
|
||
|
#ifndef HAVE_GETSERVBYNAME_R
|
||
|
QMutexLocker locker(&getXXbyYYmutex);
|
||
|
|
||
|
se = getservbyname(servname, protoname);
|
||
|
|
||
|
#else
|
||
|
size_t buflen = 1024;
|
||
|
struct servent servbuf;
|
||
|
char *buf;
|
||
|
do
|
||
|
{
|
||
|
buf = new char[buflen];
|
||
|
# ifdef USE_SOLARIS // Solaris uses a 5 argument getservbyname_r which returns struct *servent or NULL
|
||
|
if ((se = getservbyname_r(servname, protoname, &servbuf, buf, buflen)) && (errno == ERANGE))
|
||
|
# else
|
||
|
if (getservbyname_r(servname, protoname, &servbuf, buf, buflen, &se) == ERANGE)
|
||
|
# endif
|
||
|
{
|
||
|
se = 0L;
|
||
|
buflen += 1024;
|
||
|
delete [] buf;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
while (se == 0L);
|
||
|
#endif
|
||
|
|
||
|
// Do common processing
|
||
|
QStrList lst(true); // use deep copies
|
||
|
if (se != NULL)
|
||
|
{
|
||
|
lst.append(se->s_name);
|
||
|
for (char **p = se->s_aliases; *p; p++)
|
||
|
lst.append(*p);
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_GETSERVBYNAME_R
|
||
|
delete [] buf;
|
||
|
#endif
|
||
|
|
||
|
return lst;
|
||
|
}
|
||
|
|
||
|
QStrList KResolver::serviceName(int port, const char *protoname)
|
||
|
{
|
||
|
struct servent *se = 0L;
|
||
|
#ifndef HAVE_GETSERVBYPORT_R
|
||
|
QMutexLocker locker(&getXXbyYYmutex);
|
||
|
|
||
|
se = getservbyport(port, protoname);
|
||
|
|
||
|
#else
|
||
|
size_t buflen = 1024;
|
||
|
struct servent servbuf;
|
||
|
char *buf;
|
||
|
do
|
||
|
{
|
||
|
buf = new char[buflen];
|
||
|
# ifdef USE_SOLARIS // Solaris uses a 5 argument getservbyport_r which returns struct *servent or NULL
|
||
|
if ((se = getservbyport_r(port, protoname, &servbuf, buf, buflen)) && (errno == ERANGE))
|
||
|
# else
|
||
|
if (getservbyport_r(port, protoname, &servbuf, buf, buflen, &se) == ERANGE)
|
||
|
# endif
|
||
|
{
|
||
|
se = 0L;
|
||
|
buflen += 1024;
|
||
|
delete [] buf;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
while (se == 0L);
|
||
|
#endif
|
||
|
|
||
|
// Do common processing
|
||
|
QStrList lst(true); // use deep copies
|
||
|
if (se != NULL)
|
||
|
{
|
||
|
lst.append(se->s_name);
|
||
|
for (char **p = se->s_aliases; *p; p++)
|
||
|
lst.append(*p);
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_GETSERVBYPORT_R
|
||
|
delete [] buf;
|
||
|
#endif
|
||
|
|
||
|
return lst;
|
||
|
}
|
||
|
|
||
|
QString KResolver::localHostName()
|
||
|
{
|
||
|
QCString name;
|
||
|
int len;
|
||
|
|
||
|
#ifdef MAXHOSTNAMELEN
|
||
|
len = MAXHOSTNAMELEN;
|
||
|
#else
|
||
|
len = 256;
|
||
|
#endif
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
name.resize(len);
|
||
|
|
||
|
if (gethostname(name.data(), len - 1) == 0)
|
||
|
{
|
||
|
// Call succeeded, but it's not guaranteed to be NUL-terminated
|
||
|
// Note that some systems return success even if they did truncation
|
||
|
name[len - 1] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Call failed
|
||
|
if (errno == ENAMETOOLONG || errno == EINVAL)
|
||
|
len += 256;
|
||
|
else
|
||
|
{
|
||
|
// Oops! Unknown error!
|
||
|
name = QCString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (name.isEmpty())
|
||
|
return QString::fromLatin1("localhost");
|
||
|
|
||
|
if (name.find('.') == -1)
|
||
|
{
|
||
|
// not fully qualified
|
||
|
// must resolve
|
||
|
KResolverResults results = resolve(name, "0", CanonName);
|
||
|
if (results.isEmpty())
|
||
|
// cannot find a valid hostname!
|
||
|
return QString::fromLatin1("localhost");
|
||
|
else
|
||
|
return results.first().canonicalName();
|
||
|
}
|
||
|
|
||
|
return domainToUnicode(name);
|
||
|
}
|
||
|
|
||
|
|
||
|
// forward declaration
|
||
|
static QStringList splitLabels(const QString& unicodeDomain);
|
||
|
static QCString ToASCII(const QString& label);
|
||
|
static QString ToUnicode(const QString& label);
|
||
|
|
||
|
static QStringList *KResolver_initIdnDomains()
|
||
|
{
|
||
|
const char *kde_use_idn = getenv("KDE_USE_IDN");
|
||
|
if (!kde_use_idn)
|
||
|
kde_use_idn = "ac:at:br:cat:ch:cl:cn:de:dk:fi:gr:hu:info:io:is:jp:kr:li:lt:museum:org:no:se:sh:th:tm:tw:vn";
|
||
|
return new QStringList(QStringList::split(':', QString::fromLatin1(kde_use_idn).lower()));
|
||
|
}
|
||
|
|
||
|
// implement the ToAscii function, as described by IDN documents
|
||
|
QCString KResolver::domainToAscii(const QString& unicodeDomain)
|
||
|
{
|
||
|
if (!idnDomains)
|
||
|
idnDomains = KResolver_initIdnDomains();
|
||
|
|
||
|
QCString retval;
|
||
|
// RFC 3490, section 4 describes the operation:
|
||
|
// 1) this is a query, so don't allow unassigned
|
||
|
|
||
|
// 2) split the domain into individual labels, without
|
||
|
// separators.
|
||
|
QStringList input = splitLabels(unicodeDomain);
|
||
|
|
||
|
// Do we allow IDN names for this TLD?
|
||
|
if (input.count() && !idnDomains->contains(input[input.count()-1].lower()))
|
||
|
return input.join(".").lower().latin1(); // No IDN allowed for this TLD
|
||
|
|
||
|
// 3) decide whether to enforce the STD3 rules for chars < 0x7F
|
||
|
// we don't enforce
|
||
|
|
||
|
// 4) for each label, apply ToASCII
|
||
|
QStringList::Iterator it = input.begin();
|
||
|
const QStringList::Iterator end = input.end();
|
||
|
for ( ; it != end; ++it)
|
||
|
{
|
||
|
QCString cs = ToASCII(*it);
|
||
|
if (cs.isNull())
|
||
|
return QCString(); // error!
|
||
|
|
||
|
// no, all is Ok.
|
||
|
if (!retval.isEmpty())
|
||
|
retval += '.';
|
||
|
retval += cs;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
QString KResolver::domainToUnicode(const QCString& asciiDomain)
|
||
|
{
|
||
|
return domainToUnicode(QString::fromLatin1(asciiDomain));
|
||
|
}
|
||
|
|
||
|
// implement the ToUnicode function, as described by IDN documents
|
||
|
QString KResolver::domainToUnicode(const QString& asciiDomain)
|
||
|
{
|
||
|
if (asciiDomain.isEmpty())
|
||
|
return asciiDomain;
|
||
|
if (!idnDomains)
|
||
|
idnDomains = KResolver_initIdnDomains();
|
||
|
|
||
|
QString retval;
|
||
|
|
||
|
// draft-idn-idna-14.txt, section 4 describes the operation:
|
||
|
// 1) this is a query, so don't allow unassigned
|
||
|
// besides, input is ASCII
|
||
|
|
||
|
// 2) split the domain into individual labels, without
|
||
|
// separators.
|
||
|
QStringList input = splitLabels(asciiDomain);
|
||
|
|
||
|
// Do we allow IDN names for this TLD?
|
||
|
if (input.count() && !idnDomains->contains(input[input.count()-1].lower()))
|
||
|
return asciiDomain.lower(); // No TLDs allowed
|
||
|
|
||
|
// 3) decide whether to enforce the STD3 rules for chars < 0x7F
|
||
|
// we don't enforce
|
||
|
|
||
|
// 4) for each label, apply ToUnicode
|
||
|
QStringList::Iterator it;
|
||
|
const QStringList::Iterator end = input.end();
|
||
|
for (it = input.begin(); it != end; ++it)
|
||
|
{
|
||
|
QString label = ToUnicode(*it).lower();
|
||
|
|
||
|
// ToUnicode can't fail
|
||
|
if (!retval.isEmpty())
|
||
|
retval += '.';
|
||
|
retval += label;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
QString KResolver::normalizeDomain(const QString& domain)
|
||
|
{
|
||
|
return domainToUnicode(domainToAscii(domain));
|
||
|
}
|
||
|
|
||
|
void KResolver::virtual_hook( int, void* )
|
||
|
{ /*BASE::virtual_hook( id, data );*/ }
|
||
|
|
||
|
// here follows IDN functions
|
||
|
// all IDN functions conform to the following documents:
|
||
|
// RFC 3454 - Preparation of Internationalized Strings
|
||
|
// RFC 3490 - Internationalizing Domain Names in Applications (IDNA)
|
||
|
// RFC 3491 - Nameprep: A Stringprep Profile for
|
||
|
// Internationalized Domain Names (IDN
|
||
|
// RFC 3492 - Punycode: A Bootstring encoding of Unicode
|
||
|
// for Internationalized Domain Names in Applications (IDNA)
|
||
|
|
||
|
static QStringList splitLabels(const QString& unicodeDomain)
|
||
|
{
|
||
|
// From RFC 3490 section 3.1:
|
||
|
// "Whenever dots are used as label separators, the following characters
|
||
|
// MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full
|
||
|
// stop), U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full
|
||
|
// stop)."
|
||
|
static const unsigned int separators[] = { 0x002E, 0x3002, 0xFF0E, 0xFF61 };
|
||
|
|
||
|
QStringList lst;
|
||
|
int start = 0;
|
||
|
uint i;
|
||
|
for (i = 0; i < unicodeDomain.length(); i++)
|
||
|
{
|
||
|
unsigned int c = unicodeDomain[i].unicode();
|
||
|
|
||
|
if (c == separators[0] ||
|
||
|
c == separators[1] ||
|
||
|
c == separators[2] ||
|
||
|
c == separators[3])
|
||
|
{
|
||
|
// found a separator!
|
||
|
lst << unicodeDomain.mid(start, i - start);
|
||
|
start = i + 1;
|
||
|
}
|
||
|
}
|
||
|
if ((long)i >= start)
|
||
|
// there is still one left
|
||
|
lst << unicodeDomain.mid(start, i - start);
|
||
|
|
||
|
return lst;
|
||
|
}
|
||
|
|
||
|
static QCString ToASCII(const QString& label)
|
||
|
{
|
||
|
#ifdef HAVE_IDNA_H
|
||
|
// We have idna.h, so we can use the idna_to_ascii
|
||
|
// function :)
|
||
|
|
||
|
if (label.length() > 64)
|
||
|
return (char*)0L; // invalid label
|
||
|
|
||
|
if (label.length() == 0)
|
||
|
// this is allowed
|
||
|
return QCString(""); // empty, not null
|
||
|
|
||
|
QCString retval;
|
||
|
char buf[65];
|
||
|
|
||
|
Q_UINT32* ucs4 = new Q_UINT32[label.length() + 1];
|
||
|
|
||
|
uint i;
|
||
|
for (i = 0; i < label.length(); i++)
|
||
|
ucs4[i] = (unsigned long)label[i].unicode();
|
||
|
ucs4[i] = 0; // terminate with NUL, just to be on the safe side
|
||
|
|
||
|
if (idna_to_ascii_4i(ucs4, label.length(), buf, 0) == IDNA_SUCCESS)
|
||
|
// success!
|
||
|
retval = buf;
|
||
|
|
||
|
delete [] ucs4;
|
||
|
return retval;
|
||
|
#else
|
||
|
return label.latin1();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static QString ToUnicode(const QString& label)
|
||
|
{
|
||
|
#ifdef HAVE_IDNA_H
|
||
|
// We have idna.h, so we can use the idna_to_unicode
|
||
|
// function :)
|
||
|
|
||
|
Q_UINT32 *ucs4_input, *ucs4_output;
|
||
|
size_t outlen;
|
||
|
|
||
|
ucs4_input = new Q_UINT32[label.length() + 1];
|
||
|
for (uint i = 0; i < label.length(); i++)
|
||
|
ucs4_input[i] = (unsigned long)label[i].unicode();
|
||
|
|
||
|
// try the same length for output
|
||
|
ucs4_output = new Q_UINT32[outlen = label.length()];
|
||
|
|
||
|
idna_to_unicode_44i(ucs4_input, label.length(),
|
||
|
ucs4_output, &outlen,
|
||
|
0);
|
||
|
|
||
|
if (outlen > label.length())
|
||
|
{
|
||
|
// it must have failed
|
||
|
delete [] ucs4_output;
|
||
|
ucs4_output = new Q_UINT32[outlen];
|
||
|
|
||
|
idna_to_unicode_44i(ucs4_input, label.length(),
|
||
|
ucs4_output, &outlen,
|
||
|
0);
|
||
|
}
|
||
|
|
||
|
// now set the answer
|
||
|
QString result;
|
||
|
result.setLength(outlen);
|
||
|
for (uint i = 0; i < outlen; i++)
|
||
|
result[i] = (unsigned int)ucs4_output[i];
|
||
|
|
||
|
delete [] ucs4_input;
|
||
|
delete [] ucs4_output;
|
||
|
|
||
|
return result;
|
||
|
#else
|
||
|
return label;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#include "kresolver.moc"
|