diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/network/kernel | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'src/network/kernel')
25 files changed, 9030 insertions, 0 deletions
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri new file mode 100644 index 0000000..8aa6ff4 --- /dev/null +++ b/src/network/kernel/kernel.pri @@ -0,0 +1,30 @@ +# Qt network kernel module + +PRECOMPILED_HEADER = ../corelib/global/qt_pch.h +INCLUDEPATH += $$PWD + +HEADERS += kernel/qauthenticator.h \ + kernel/qauthenticator_p.h \ + kernel/qhostaddress.h \ + kernel/qhostinfo.h \ + kernel/qhostinfo_p.h \ + kernel/qurlinfo.h \ + kernel/qnetworkproxy.h \ + kernel/qnetworkinterface.h \ + kernel/qnetworkinterface_p.h + +SOURCES += kernel/qauthenticator.cpp \ + kernel/qhostaddress.cpp \ + kernel/qhostinfo.cpp \ + kernel/qurlinfo.cpp \ + kernel/qnetworkproxy.cpp \ + kernel/qnetworkinterface.cpp + +unix:SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp +win32:SOURCES += kernel/qhostinfo_win.cpp kernel/qnetworkinterface_win.cpp + +mac:LIBS+= -framework SystemConfiguration +mac:SOURCES += kernel/qnetworkproxy_mac.cpp +else:win32:SOURCES += kernel/qnetworkproxy_win.cpp +else:SOURCES += kernel/qnetworkproxy_generic.cpp + diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp new file mode 100644 index 0000000..4f477bd --- /dev/null +++ b/src/network/kernel/qauthenticator.cpp @@ -0,0 +1,1026 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qauthenticator.h> +#include <qauthenticator_p.h> +#include <qdebug.h> +#include <qhash.h> +#include <qbytearray.h> +#include <qcryptographichash.h> +#include <qhttp.h> +#include <qdatastream.h> +#include <qendian.h> +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +#include <../3rdparty/des/des.cpp> + +static QByteArray qNtlmPhase1(); +static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data); + +/*! + \class QAuthenticator + \brief The QAuthenticator class provides an authentication object. + \since 4.3 + + \reentrant + \ingroup io + \inmodule QtNetwork + + The QAuthenticator class is usually used in the + \l{QHttp::}{authenticationRequired()} and + \l{QHttp::}{proxyAuthenticationRequired()} signals of QHttp and + QAbstractSocket. The class provides a way to pass back the required + authentication information to the socket when accessing services that + require authentication. + + \sa QSslSocket +*/ + + +/*! + Constructs an empty authentication object +*/ +QAuthenticator::QAuthenticator() + : d(0) +{ +} + +/*! + Destructs the object +*/ +QAuthenticator::~QAuthenticator() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Constructs a copy of \a other. +*/ +QAuthenticator::QAuthenticator(const QAuthenticator &other) + : d(other.d) +{ + if (d) + d->ref.ref(); +} + +/*! + Assigns the contents of \a other to this authenticator. +*/ +QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other) +{ + if (d == other.d) + return *this; + detach(); + d->user = other.d->user; + d->password = other.d->password; + return *this; +} + +/*! + Returns true if this authenticator is identical to \a other; otherwise + returns false. +*/ +bool QAuthenticator::operator==(const QAuthenticator &other) const +{ + if (d == other.d) + return true; + return d->user == other.d->user + && d->password == other.d->password + && d->realm == other.d->realm + && d->method == other.d->method; +} + +/*! + \fn bool QAuthenticator::operator!=(const QAuthenticator &other) const + + Returns true if this authenticator is different from \a other; otherwise + returns false. +*/ + +/*! + returns the user used for authentication. +*/ +QString QAuthenticator::user() const +{ + return d ? d->user : QString(); +} + +/*! + Sets the \a user used for authentication. +*/ +void QAuthenticator::setUser(const QString &user) +{ + detach(); + d->user = user; +} + +/*! + returns the password used for authentication. +*/ +QString QAuthenticator::password() const +{ + return d ? d->password : QString(); +} + +/*! + Sets the \a password used for authentication. +*/ +void QAuthenticator::setPassword(const QString &password) +{ + detach(); + d->password = password; +} + +/*! + \internal +*/ +void QAuthenticator::detach() +{ + if (!d) { + d = new QAuthenticatorPrivate; + d->ref = 1; + return; + } + + qAtomicDetach(d); + d->phase = QAuthenticatorPrivate::Start; +} + +/*! + returns the realm requiring authentication. +*/ +QString QAuthenticator::realm() const +{ + return d ? d->realm : QString(); +} + + +/*! + returns true if the authenticator is null. +*/ +bool QAuthenticator::isNull() const +{ + return !d; +} + +QAuthenticatorPrivate::QAuthenticatorPrivate() + : ref(0) + , method(None) + , phase(Start) + , nonceCount(0) +{ + cnonce = QCryptographicHash::hash(QByteArray::number(qrand(), 16) + QByteArray::number(qrand(), 16), + QCryptographicHash::Md5).toHex(); + nonceCount = 0; +} + +#ifndef QT_NO_HTTP +void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy) +{ + QList<QPair<QString, QString> > values = header.values(); + const char *search = isProxy ? "proxy-authenticate" : "www-authenticate"; + + method = None; + /* + Fun from the HTTP 1.1 specs, that we currently ignore: + + User agents are advised to take special care in parsing the WWW- + Authenticate field value as it might contain more than one challenge, + or if more than one WWW-Authenticate header field is provided, the + contents of a challenge itself can contain a comma-separated list of + authentication parameters. + */ + + QString headerVal; + for (int i = 0; i < values.size(); ++i) { + const QPair<QString, QString> ¤t = values.at(i); + if (current.first.toLower() != QLatin1String(search)) + continue; + QString str = current.second; + if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) { + method = Basic; headerVal = str.mid(6); + } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) { + method = Ntlm; + headerVal = str.mid(5); + } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) { + method = DigestMd5; + headerVal = str.mid(7); + } + } + + challenge = headerVal.trimmed().toLatin1(); + QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); + + switch(method) { + case Basic: + realm = QString::fromLatin1(options.value("realm")); + if (user.isEmpty()) + phase = Done; + break; + case Ntlm: + // #### extract from header + realm = QString(); + break; + case DigestMd5: { + realm = QString::fromLatin1(options.value("realm")); + if (options.value("stale").toLower() == "true") + phase = Start; + if (user.isEmpty()) + phase = Done; + break; + } + default: + realm = QString(); + challenge = QByteArray(); + phase = Invalid; + } +} +#endif + +QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path) +{ + QByteArray response; + const char *methodString = 0; + switch(method) { + case QAuthenticatorPrivate::None: + methodString = ""; + phase = Done; + break; + case QAuthenticatorPrivate::Plain: + response = '\0' + user.toUtf8() + '\0' + password.toUtf8(); + phase = Done; + break; + case QAuthenticatorPrivate::Basic: + methodString = "Basic "; + response = user.toLatin1() + ':' + password.toLatin1(); + response = response.toBase64(); + phase = Done; + break; + case QAuthenticatorPrivate::Login: + if (challenge.contains("VXNlciBOYW1lAA==")) { + response = user.toUtf8().toBase64(); + phase = Phase2; + } else if (challenge.contains("UGFzc3dvcmQA")) { + response = password.toUtf8().toBase64(); + phase = Done; + } + break; + case QAuthenticatorPrivate::CramMd5: + break; + case QAuthenticatorPrivate::DigestMd5: + methodString = "Digest "; + response = digestMd5Response(challenge, requestMethod, path); + phase = Done; + break; + case QAuthenticatorPrivate::Ntlm: + methodString = "NTLM "; + if (challenge.isEmpty()) { + response = qNtlmPhase1().toBase64(); + if (user.isEmpty()) + phase = Done; + else + phase = Phase2; + } else { + response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64(); + phase = Done; + } + + break; + } + return QByteArray(methodString) + response; +} + + +// ---------------------------- Digest Md5 code ---------------------------------------- + +QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge) +{ + QHash<QByteArray, QByteArray> options; + // parse the challenge + const char *d = challenge.constData(); + const char *end = d + challenge.length(); + while (d < end) { + while (d < end && (*d == ' ' || *d == '\n' || *d == '\r')) + ++d; + const char *start = d; + while (d < end && *d != '=') + ++d; + QByteArray key = QByteArray(start, d - start); + ++d; + if (d >= end) + break; + bool quote = (*d == '"'); + if (quote) + ++d; + if (d >= end) + break; + start = d; + QByteArray value; + while (d < end) { + bool backslash = false; + if (*d == '\\' && d < end - 1) { + ++d; + backslash = true; + } + if (!backslash) { + if (quote) { + if (*d == '"') + break; + } else { + if (*d == ',') + break; + } + } + value += *d; + ++d; + } + while (d < end && *d != ',') + ++d; + ++d; + options[key] = value; + } + + QByteArray qop = options.value("qop"); + if (!qop.isEmpty()) { + QList<QByteArray> qopoptions = qop.split(','); + if (!qopoptions.contains("auth")) + return QHash<QByteArray, QByteArray>(); + // #### can't do auth-int currently +// if (qop.contains("auth-int")) +// qop = "auth-int"; +// else if (qop.contains("auth")) +// qop = "auth"; +// else +// qop = QByteArray(); + options["qop"] = "auth"; + } + + return options; +} + +/* + Digest MD5 implementation + + Code taken from RFC 2617 + + Currently we don't support the full SASL authentication mechanism (which includes cyphers) +*/ + + +/* calculate request-digest/response-digest as per HTTP Digest spec */ +static QByteArray digestMd5ResponseHelper( + const QByteArray &alg, + const QByteArray &userName, + const QByteArray &realm, + const QByteArray &password, + const QByteArray &nonce, /* nonce from server */ + const QByteArray &nonceCount, /* 8 hex digits */ + const QByteArray &cNonce, /* client nonce */ + const QByteArray &qop, /* qop-value: "", "auth", "auth-int" */ + const QByteArray &method, /* method from the request */ + const QByteArray &digestUri, /* requested URL */ + const QByteArray &hEntity /* H(entity body) if qop="auth-int" */ + ) +{ + QCryptographicHash hash(QCryptographicHash::Md5); + hash.addData(userName); + hash.addData(":", 1); + hash.addData(realm); + hash.addData(":", 1); + hash.addData(password); + QByteArray ha1 = hash.result(); + if (alg.toLower() == "md5-sess") { + hash.reset(); + // RFC 2617 contains an error, it was: + // hash.addData(ha1); + // but according to the errata page at http://www.rfc-editor.org/errata_list.php, ID 1649, it + // must be the following line: + hash.addData(ha1.toHex()); + hash.addData(":", 1); + hash.addData(nonce); + hash.addData(":", 1); + hash.addData(cNonce); + ha1 = hash.result(); + }; + ha1 = ha1.toHex(); + + // calculate H(A2) + hash.reset(); + hash.addData(method); + hash.addData(":", 1); + hash.addData(digestUri); + if (qop.toLower() == "auth-int") { + hash.addData(":", 1); + hash.addData(hEntity); + } + QByteArray ha2hex = hash.result().toHex(); + + // calculate response + hash.reset(); + hash.addData(ha1); + hash.addData(":", 1); + hash.addData(nonce); + hash.addData(":", 1); + if (!qop.isNull()) { + hash.addData(nonceCount); + hash.addData(":", 1); + hash.addData(cNonce); + hash.addData(":", 1); + hash.addData(qop); + hash.addData(":", 1); + } + hash.addData(ha2hex); + return hash.result().toHex(); +} + +QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path) +{ + QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge); + + ++nonceCount; + QByteArray nonceCountString = QByteArray::number(nonceCount, 16); + while (nonceCountString.length() < 8) + nonceCountString.prepend('0'); + + QByteArray nonce = options.value("nonce"); + QByteArray opaque = options.value("opaque"); + QByteArray qop = options.value("qop"); + +// qDebug() << "calculating digest: method=" << method << "path=" << path; + QByteArray response = digestMd5ResponseHelper(options.value("algorithm"), user.toLatin1(), + realm.toLatin1(), password.toLatin1(), + nonce, nonceCountString, + cnonce, qop, method, + path, QByteArray()); + + + QByteArray credentials; + credentials += "username=\"" + user.toLatin1() + "\", "; + credentials += "realm=\"" + realm.toLatin1() + "\", "; + credentials += "nonce=\"" + nonce + "\", "; + credentials += "uri=\"" + path + "\", "; + if (!opaque.isEmpty()) + credentials += "opaque=\"" + opaque + "\", "; + credentials += "response=\"" + response + "\""; + if (!options.value("algorithm").isEmpty()) + credentials += ", algorithm=" + options.value("algorithm"); + if (!options.value("qop").isEmpty()) { + credentials += ", qop=" + qop + ", "; + credentials += "nc=" + nonceCountString + ", "; + credentials += "cnonce=\"" + cnonce + "\""; + } + + return credentials; +} + +// ---------------------------- Digest Md5 code ---------------------------------------- + + + +/* + * NTLM message flags. + * + * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru> + * + * This software is released under the MIT license. + */ + +/* + * Indicates that Unicode strings are supported for use in security + * buffer data. + */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 + +/* + * Indicates that OEM strings are supported for use in security buffer data. + */ +#define NTLMSSP_NEGOTIATE_OEM 0x00000002 + +/* + * Requests that the server's authentication realm be included in the + * Type 2 message. + */ +#define NTLMSSP_REQUEST_TARGET 0x00000004 + +/* + * Specifies that authenticated communication between the client and server + * should carry a digital signature (message integrity). + */ +#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 + +/* + * Specifies that authenticated communication between the client and server + * should be encrypted (message confidentiality). + */ +#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 + +/* + * Indicates that datagram authentication is being used. + */ +#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 + +/* + * Indicates that the LAN Manager session key should be + * used for signing and sealing authenticated communications. + */ +#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 + +/* + * Indicates that NTLM authentication is being used. + */ +#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 + +/* + * Sent by the client in the Type 1 message to indicate that the name of the + * domain in which the client workstation has membership is included in the + * message. This is used by the server to determine whether the client is + * eligible for local authentication. + */ +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 + +/* + * Sent by the client in the Type 1 message to indicate that the client + * workstation's name is included in the message. This is used by the server + * to determine whether the client is eligible for local authentication. + */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 + +/* + * Sent by the server to indicate that the server and client are on the same + * machine. Implies that the client may use the established local credentials + * for authentication instead of calculating a response to the challenge. + */ +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000 + +/* + * Indicates that authenticated communication between the client and server + * should be signed with a "dummy" signature. + */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 + +/* + * Sent by the server in the Type 2 message to indicate that the target + * authentication realm is a domain. + */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 + +/* + * Sent by the server in the Type 2 message to indicate that the target + * authentication realm is a server. + */ +#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 + +/* + * Sent by the server in the Type 2 message to indicate that the target + * authentication realm is a share. Presumably, this is for share-level + * authentication. Usage is unclear. + */ +#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000 + +/* + * Indicates that the NTLM2 signing and sealing scheme should be used for + * protecting authenticated communications. Note that this refers to a + * particular session security scheme, and is not related to the use of + * NTLMv2 authentication. + */ +#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 + +/* + * Sent by the server in the Type 2 message to indicate that it is including + * a Target Information block in the message. The Target Information block + * is used in the calculation of the NTLMv2 response. + */ +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 + +/* + * Indicates that 128-bit encryption is supported. + */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 + +/* + * Indicates that the client will provide an encrypted master session key in + * the "Session Key" field of the Type 3 message. This is used in signing and + * sealing, and is RC4-encrypted using the previous session key as the + * encryption key. + */ +#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000 + +/* + * Indicates that 56-bit encryption is supported. + */ +#define NTLMSSP_NEGOTIATE_56 0x80000000 + + +/* usage: + // fill up ctx with what we know. + QByteArray response = qNtlmPhase1(ctx); + // send response (b64 encoded??) + // get response from server (b64 decode?) + Phase2Block pb; + qNtlmDecodePhase2(response, pb); + response = qNtlmPhase3(ctx, pb); + // send response (b64 encoded??) +*/ + +/* + TODO: + - Fix unicode handling + - add v2 handling +*/ + +class QNtlmBuffer { +public: + QNtlmBuffer() : len(0), maxLen(0), offset(0) {} + quint16 len; + quint16 maxLen; + quint32 offset; + enum { Size = 8 }; +}; + +class QNtlmPhase1BlockBase +{ +public: + char magic[8]; + quint32 type; + quint32 flags; + QNtlmBuffer domain; + QNtlmBuffer workstation; + enum { Size = 32 }; +}; + +// ################# check paddings +class QNtlmPhase2BlockBase +{ +public: + char magic[8]; + quint32 type; + QNtlmBuffer targetName; + quint32 flags; + unsigned char challenge[8]; + quint32 context[2]; + QNtlmBuffer targetInfo; + enum { Size = 48 }; +}; + +class QNtlmPhase3BlockBase { +public: + char magic[8]; + quint32 type; + QNtlmBuffer lmResponse; + QNtlmBuffer ntlmResponse; + QNtlmBuffer domain; + QNtlmBuffer user; + QNtlmBuffer workstation; + QNtlmBuffer sessionKey; + quint32 flags; + enum { Size = 64 }; +}; + +static void qStreamNtlmBuffer(QDataStream& ds, const QByteArray& s) +{ + ds.writeRawData(s.constData(), s.size()); +} + + +static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode) +{ + if (!unicode) { + qStreamNtlmBuffer(ds, s.toLatin1()); + return; + } + const ushort *d = s.utf16(); + for (int i = 0; i < s.length(); ++i) + ds << d[i]; +} + + + +static int qEncodeNtlmBuffer(QNtlmBuffer& buf, int offset, const QByteArray& s) +{ + buf.len = s.size(); + buf.maxLen = buf.len; + buf.offset = (offset + 1) & ~1; + return buf.offset + buf.len; +} + + +static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, bool unicode) +{ + if (!unicode) + return qEncodeNtlmBuffer(buf, offset, s.toLatin1()); + buf.len = 2 * s.length(); + buf.maxLen = buf.len; + buf.offset = (offset + 1) & ~1; + return buf.offset + buf.len; +} + + +static QDataStream& operator<<(QDataStream& s, const QNtlmBuffer& b) +{ + s << b.len << b.maxLen << b.offset; + return s; +} + +static QDataStream& operator>>(QDataStream& s, QNtlmBuffer& b) +{ + s >> b.len >> b.maxLen >> b.offset; + return s; +} + + +class QNtlmPhase1Block : public QNtlmPhase1BlockBase +{ // request +public: + QNtlmPhase1Block() { + qstrncpy(magic, "NTLMSSP", 8); + type = 1; + flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET; + } + + // extracted + QString domainStr, workstationStr; +}; + + +class QNtlmPhase2Block : public QNtlmPhase2BlockBase +{ // challenge +public: + QNtlmPhase2Block() { + magic[0] = 0; + type = 0xffffffff; + } + + // extracted + QString targetNameStr, targetInfoStr; +}; + + + +class QNtlmPhase3Block : public QNtlmPhase3BlockBase { // response +public: + QNtlmPhase3Block() { + qstrncpy(magic, "NTLMSSP", 8); + type = 3; + flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO; + } + + // extracted + QByteArray lmResponseBuf, ntlmResponseBuf; + QString domainStr, userStr, workstationStr, sessionKeyStr; +}; + + +static QDataStream& operator<<(QDataStream& s, const QNtlmPhase1Block& b) { + bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE); + + s.writeRawData(b.magic, sizeof(b.magic)); + s << b.type; + s << b.flags; + s << b.domain; + s << b.workstation; + if (!b.domainStr.isEmpty()) + qStreamNtlmString(s, b.domainStr, unicode); + if (!b.workstationStr.isEmpty()) + qStreamNtlmString(s, b.workstationStr, unicode); + return s; +} + + +static QDataStream& operator<<(QDataStream& s, const QNtlmPhase3Block& b) { + bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE); + s.writeRawData(b.magic, sizeof(b.magic)); + s << b.type; + s << b.lmResponse; + s << b.ntlmResponse; + s << b.domain; + s << b.user; + s << b.workstation; + s << b.sessionKey; + s << b.flags; + + if (!b.domainStr.isEmpty()) + qStreamNtlmString(s, b.domainStr, unicode); + + qStreamNtlmString(s, b.userStr, unicode); + + if (!b.workstationStr.isEmpty()) + qStreamNtlmString(s, b.workstationStr, unicode); + + // Send auth info + qStreamNtlmBuffer(s, b.lmResponseBuf); + qStreamNtlmBuffer(s, b.ntlmResponseBuf); + + + return s; +} + + +static QByteArray qNtlmPhase1() +{ + QByteArray rc; + QDataStream ds(&rc, QIODevice::WriteOnly); + ds.setByteOrder(QDataStream::LittleEndian); + QNtlmPhase1Block pb; + ds << pb; + return rc; +} + + +static QByteArray qStringAsUcs2Le(const QString& src) +{ + QByteArray rc(2*src.length(), 0); + const unsigned short *s = src.utf16(); + unsigned short *d = (unsigned short*)rc.data(); + for (int i = 0; i < src.length(); ++i) { + d[i] = qToLittleEndian(s[i]); + } + return rc; +} + + +static QString qStringFromUcs2Le(const QByteArray& src) +{ + Q_ASSERT(src.size() % 2 == 0); + unsigned short *d = (unsigned short*)src.data(); + for (int i = 0; i < src.length() / 2; ++i) { + d[i] = qFromLittleEndian(d[i]); + } + return QString((const QChar *)src.data(), src.size()/2); +} + + +static QByteArray qEncodeNtlmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch) +{ + QCryptographicHash md4(QCryptographicHash::Md4); + QByteArray asUcs2Le = qStringAsUcs2Le(ctx->password); + md4.addData(asUcs2Le.data(), asUcs2Le.size()); + + unsigned char md4hash[22]; + memset(md4hash, 0, sizeof(md4hash)); + QByteArray hash = md4.result(); + Q_ASSERT(hash.size() == 16); + memcpy(md4hash, hash.constData(), 16); + + QByteArray rc(24, 0); + deshash((unsigned char *)rc.data(), md4hash, (unsigned char *)ch.challenge); + deshash((unsigned char *)rc.data() + 8, md4hash + 7, (unsigned char *)ch.challenge); + deshash((unsigned char *)rc.data() + 16, md4hash + 14, (unsigned char *)ch.challenge); + + hash.fill(0); + return rc; +} + + +static QByteArray qEncodeLmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch) +{ + QByteArray hash(21, 0); + QByteArray key(14, 0); + qstrncpy(key.data(), ctx->password.toUpper().toLatin1(), 14); + const char *block = "KGS!@#$%"; + + deshash((unsigned char *)hash.data(), (unsigned char *)key.data(), (unsigned char *)block); + deshash((unsigned char *)hash.data() + 8, (unsigned char *)key.data() + 7, (unsigned char *)block); + key.fill(0); + + QByteArray rc(24, 0); + deshash((unsigned char *)rc.data(), (unsigned char *)hash.data(), ch.challenge); + deshash((unsigned char *)rc.data() + 8, (unsigned char *)hash.data() + 7, ch.challenge); + deshash((unsigned char *)rc.data() + 16, (unsigned char *)hash.data() + 14, ch.challenge); + + hash.fill(0); + return rc; +} + + +static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch) +{ + Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase)); + if (data.size() < QNtlmPhase2BlockBase::Size) + return false; + + + QDataStream ds(data); + ds.setByteOrder(QDataStream::LittleEndian); + if (ds.readRawData(ch.magic, 8) < 8) + return false; + if (strncmp(ch.magic, "NTLMSSP", 8) != 0) + return false; + + ds >> ch.type; + if (ch.type != 2) + return false; + + ds >> ch.targetName; + ds >> ch.flags; + if (ds.readRawData((char *)ch.challenge, 8) < 8) + return false; + ds >> ch.context[0] >> ch.context[1]; + ds >> ch.targetInfo; + + if (ch.targetName.len > 0) { + if (ch.targetName.len + ch.targetName.offset >= (unsigned)data.size()) + return false; + + ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len)); + } + + if (ch.targetInfo.len > 0) { + // UNUSED right now + } + + return true; +} + + +static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data) +{ + QNtlmPhase2Block ch; + if (!qNtlmDecodePhase2(phase2data, ch)) + return QByteArray(); + + QByteArray rc; + QDataStream ds(&rc, QIODevice::WriteOnly); + ds.setByteOrder(QDataStream::LittleEndian); + QNtlmPhase3Block pb; + + bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE; + + ctx->realm = ch.targetNameStr; + + pb.flags = NTLMSSP_NEGOTIATE_NTLM; + if (unicode) + pb.flags |= NTLMSSP_NEGOTIATE_UNICODE; + else + pb.flags |= NTLMSSP_NEGOTIATE_OEM; + + + int offset = QNtlmPhase3BlockBase::Size; + Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase)); + + offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode); + pb.domainStr = ctx->realm; + offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode); + pb.userStr = ctx->user; + + offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode); + pb.workstationStr = ctx->workstation; + + // Get LM response + pb.lmResponseBuf = qEncodeLmResponse(ctx, ch); + offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf); + + // Get NTLM response + pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch); + offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf); + + + // Encode and send + ds << pb; + + return rc; +} + + + +QT_END_NAMESPACE diff --git a/src/network/kernel/qauthenticator.h b/src/network/kernel/qauthenticator.h new file mode 100644 index 0000000..aa78a56 --- /dev/null +++ b/src/network/kernel/qauthenticator.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUTHENTICATOR_H +#define QAUTHENTICATOR_H + +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +class QAuthenticatorPrivate; +class QUrl; + +class Q_NETWORK_EXPORT QAuthenticator +{ +public: + QAuthenticator(); + ~QAuthenticator(); + + QAuthenticator(const QAuthenticator &other); + QAuthenticator &operator=(const QAuthenticator &other); + + bool operator==(const QAuthenticator &other) const; + inline bool operator!=(const QAuthenticator &other) const { return !operator==(other); } + + QString user() const; + void setUser(const QString &user); + + QString password() const; + void setPassword(const QString &password); + + QString realm() const; + + bool isNull() const; + void detach(); +private: + friend class QAuthenticatorPrivate; + QAuthenticatorPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h new file mode 100644 index 0000000..42583cf --- /dev/null +++ b/src/network/kernel/qauthenticator_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUTHENTICATOR_P_H +#define QAUTHENTICATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qhash.h> +#include <qbytearray.h> +#include <qstring.h> +#include <qauthenticator.h> + +QT_BEGIN_NAMESPACE + +class QHttpResponseHeader; + +class QAuthenticatorPrivate +{ +public: + enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 }; + QAuthenticatorPrivate(); + + QAtomicInt ref; + QString user; + QString password; + QHash<QByteArray, QByteArray> options; + Method method; + QString realm; + QByteArray challenge; + + enum Phase { + Start, + Phase2, + Done, + Invalid + }; + Phase phase; + + // digest specific + QByteArray cnonce; + int nonceCount; + + // ntlm specific + QString workstation; + + QByteArray calculateResponse(const QByteArray &method, const QByteArray &path); + + inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; } + inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; } + + QByteArray digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path); + static QHash<QByteArray, QByteArray> parseDigestAuthenticationChallenge(const QByteArray &challenge); + +#ifndef QT_NO_HTTP + void parseHttpResponse(const QHttpResponseHeader &, bool isProxy); +#endif + +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp new file mode 100644 index 0000000..fc43369 --- /dev/null +++ b/src/network/kernel/qhostaddress.cpp @@ -0,0 +1,1166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhostaddress.h" +#include "qhostaddress_p.h" +#include "qdebug.h" +#include "qplatformdefs.h" +#include "qstringlist.h" +#include "qendian.h" +#ifndef QT_NO_DATASTREAM +#include <qdatastream.h> +#endif +#if defined(Q_OS_WINCE) +#include <winsock.h> +#endif + +#ifdef QT_LINUXBASE +# include <arpa/inet.h> +#endif + +QT_BEGIN_NAMESPACE + +#define QT_ENSURE_PARSED(a) \ + do { \ + if (!(a)->d->isParsed) \ + (a)->d->parse(); \ + } while (0) + +#ifdef Q_OS_WIN +# if !defined (QT_NO_IPV6) +// sockaddr_in6 size changed between old and new SDK +// Only the new version is the correct one, so always +// use this structure. +#if defined(Q_OS_WINCE) +# if !defined(u_char) +# define u_char unsigned char +# endif +# if !defined(u_short) +# define u_short unsigned short +# endif +# if !defined(u_long) +# define u_long unsigned long +# endif +#endif +struct qt_in6_addr { + u_char qt_s6_addr[16]; +}; +typedef struct { + short sin6_family; /* AF_INET6 */ + u_short sin6_port; /* Transport level port number */ + u_long sin6_flowinfo; /* IPv6 flow information */ + struct qt_in6_addr sin6_addr; /* IPv6 address */ + u_long sin6_scope_id; /* set of interfaces for a scope */ +} qt_sockaddr_in6; +# else +typedef void * qt_sockaddr_in6 ; +# endif +# ifndef AF_INET6 +# define AF_INET6 23 /* Internetwork Version 6 */ +# endif +#else +#define qt_sockaddr_in6 sockaddr_in6 +#define qt_s6_addr s6_addr +#endif + + +class QHostAddressPrivate +{ +public: + QHostAddressPrivate(); + + void setAddress(quint32 a_ = 0); + void setAddress(const quint8 *a_); + void setAddress(const Q_IPV6ADDR &a_); + + bool parse(); + void clear(); + + quint32 a; // IPv4 address + Q_IPV6ADDR a6; // IPv6 address + QAbstractSocket::NetworkLayerProtocol protocol; + + QString ipString; + bool isParsed; + QString scopeId; + + friend class QHostAddress; +}; + +QHostAddressPrivate::QHostAddressPrivate() + : a(0), protocol(QAbstractSocket::UnknownNetworkLayerProtocol), isParsed(true) +{ + memset(&a6, 0, sizeof(a6)); +} + +void QHostAddressPrivate::setAddress(quint32 a_) +{ + a = a_; + protocol = QAbstractSocket::IPv4Protocol; + isParsed = true; +} + +void QHostAddressPrivate::setAddress(const quint8 *a_) +{ + for (int i = 0; i < 16; i++) + a6[i] = a_[i]; + protocol = QAbstractSocket::IPv6Protocol; + isParsed = true; +} + +void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_) +{ + a6 = a_; + a = 0; + protocol = QAbstractSocket::IPv6Protocol; + isParsed = true; +} + +static bool parseIp4(const QString& address, quint32 *addr) +{ + QStringList ipv4 = address.split(QLatin1String(".")); + if (ipv4.count() != 4) + return false; + + quint32 ipv4Address = 0; + for (int i = 0; i < 4; ++i) { + bool ok = false; + uint byteValue = ipv4.at(i).toUInt(&ok); + if (!ok || byteValue > 255) + return false; + + ipv4Address <<= 8; + ipv4Address += byteValue; + } + + *addr = ipv4Address; + return true; +} + +static bool parseIp6(const QString &address, quint8 *addr, QString *scopeId) +{ + QString tmp = address; + int scopeIdPos = tmp.lastIndexOf(QLatin1Char('%')); + if (scopeIdPos != -1) { + *scopeId = tmp.mid(scopeIdPos + 1); + tmp.chop(tmp.size() - scopeIdPos); + } else { + scopeId->clear(); + } + + QStringList ipv6 = tmp.split(QLatin1String(":")); + int count = ipv6.count(); + if (count < 3 || count > 8) + return false; + + int colonColon = tmp.count(QLatin1String("::")); + if(count == 8 && colonColon > 1) + return false; + + // address can be compressed with a "::", but that + // may only appear once (see RFC 1884) + // the statement below means: + // if(shortened notation is not used AND + // ((pure IPv6 notation AND less than 8 parts) OR + // ((mixed IPv4/6 notation AND less than 7 parts))) + if(colonColon != 1 && count < (tmp.contains(QLatin1Char('.')) ? 7 : 8)) + return false; + + int mc = 16; + int fillCount = 9 - count; // number of 0 words to fill in the middle + for (int i = count - 1; i >= 0; --i) { + if (mc <= 0) + return false; + + if (ipv6.at(i).isEmpty()) { + if (i == count - 1) { + // special case: ":" is last character + if (!ipv6.at(i - 1).isEmpty()) + return false; + addr[--mc] = 0; + addr[--mc] = 0; + } else if (i == 0) { + // special case: ":" is first character + if (!ipv6.at(i + 1).isEmpty()) + return false; + addr[--mc] = 0; + addr[--mc] = 0; + } else { + for (int j = 0; j < fillCount; ++j) { + if (mc <= 0) + return false; + addr[--mc] = 0; + addr[--mc] = 0; + } + } + } else { + bool ok = false; + uint byteValue = ipv6.at(i).toUInt(&ok, 16); + if (ok && byteValue <= 0xffff) { + addr[--mc] = byteValue & 0xff; + addr[--mc] = (byteValue >> 8) & 0xff; + } else { + if (i != count - 1) + return false; + + // parse the ipv4 part of a mixed type + quint32 maybeIp4; + if (!parseIp4(ipv6.at(i), &maybeIp4)) + return false; + + addr[--mc] = maybeIp4 & 0xff; + addr[--mc] = (maybeIp4 >> 8) & 0xff; + addr[--mc] = (maybeIp4 >> 16) & 0xff; + addr[--mc] = (maybeIp4 >> 24) & 0xff; + --fillCount; + } + } + } + + return true; +} + +bool QHostAddressPrivate::parse() +{ + isParsed = true; + protocol = QAbstractSocket::UnknownNetworkLayerProtocol; + QString a = ipString.simplified(); + + // All IPv6 addresses contain a ':', and may contain a '.'. + if (a.contains(QLatin1Char(':'))) { + quint8 maybeIp6[16]; + if (parseIp6(a, maybeIp6, &scopeId)) { + setAddress(maybeIp6); + protocol = QAbstractSocket::IPv6Protocol; + return true; + } + } + + // All IPv4 addresses contain a '.'. + if (a.contains(QLatin1Char('.'))) { + quint32 maybeIp4 = 0; + if (parseIp4(a, &maybeIp4)) { + setAddress(maybeIp4); + protocol = QAbstractSocket::IPv4Protocol; + return true; + } + } + + return false; +} + +void QHostAddressPrivate::clear() +{ + a = 0; + protocol = QAbstractSocket::UnknownNetworkLayerProtocol; + isParsed = true; + memset(&a6, 0, sizeof(a6)); +} + + +bool QNetmaskAddress::setAddress(const QString &address) +{ + length = -1; + QHostAddress other; + return other.setAddress(address) && setAddress(other); +} + +bool QNetmaskAddress::setAddress(const QHostAddress &address) +{ + static const quint8 zeroes[16] = { 0 }; + union { + quint32 v4; + quint8 v6[16]; + } ip; + + int netmask = 0; + quint8 *ptr = ip.v6; + quint8 *end; + length = -1; + + QHostAddress::operator=(address); + + if (d->protocol == QAbstractSocket::IPv4Protocol) { + ip.v4 = qToBigEndian(d->a); + end = ptr + 4; + } else if (d->protocol == QAbstractSocket::IPv6Protocol) { + memcpy(ip.v6, d->a6.c, 16); + end = ptr + 16; + } else { + d->clear(); + return false; + } + + while (ptr < end) { + switch (*ptr) { + case 255: + netmask += 8; + ++ptr; + continue; + + default: + d->clear(); + return false; // invalid IP-style netmask + + // the rest always falls through + case 254: + ++netmask; + case 252: + ++netmask; + case 248: + ++netmask; + case 240: + ++netmask; + case 224: + ++netmask; + case 192: + ++netmask; + case 128: + ++netmask; + case 0: + break; + } + break; + } + + // confirm that the rest is only zeroes + if (ptr < end && memcmp(ptr + 1, zeroes, end - ptr - 1) != 0) { + d->clear(); + return false; + } + + length = netmask; + return true; +} + +static void clearBits(quint8 *where, int start, int end) +{ + Q_ASSERT(end == 32 || end == 128); + if (start == end) + return; + + // for the byte where 'start' is, clear the lower bits only + quint8 bytemask = 256 - (1 << (8 - (start & 7))); + where[start / 8] &= bytemask; + + // for the tail part, clear everything + memset(where + (start + 7) / 8, 0, end / 8 - (start + 7) / 8); +} + +int QNetmaskAddress::prefixLength() const +{ + return length; +} + +void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int newLength) +{ + length = newLength; + if (length < 0 || length > (proto == QAbstractSocket::IPv4Protocol ? 32 : + proto == QAbstractSocket::IPv6Protocol ? 128 : -1)) { + // invalid information, reject + d->protocol = QAbstractSocket::UnknownNetworkLayerProtocol; + length = -1; + return; + } + + d->protocol = proto; + if (d->protocol == QAbstractSocket::IPv4Protocol) { + if (length == 0) { + d->a = 0; + } else if (length == 32) { + d->a = quint32(0xffffffff); + } else { + d->a = quint32(0xffffffff) >> (32 - length) << (32 - length); + } + } else { + memset(d->a6.c, 0xFF, sizeof(d->a6)); + clearBits(d->a6.c, length, 128); + } +} + +/*! + \class QHostAddress + \brief The QHostAddress class provides an IP address. + \ingroup io + \inmodule QtNetwork + + This class holds an IPv4 or IPv6 address in a platform- and + protocol-independent manner. + + QHostAddress is normally used with the QTcpSocket, QTcpServer, + and QUdpSocket to connect to a host or to set up a server. + + A host address is set with setAddress(), checked for its type + using isIPv4Address() or isIPv6Address(), and retrieved with + toIPv4Address(), toIPv6Address(), or toString(). + + The class also supports common predefined addresses: \l Null, \l + LocalHost, \l LocalHostIPv6, \l Broadcast, and \l Any. + + \sa QTcpSocket, QTcpServer, QUdpSocket +*/ + +/*! \enum QHostAddress::SpecialAddress + + \value Null The null address object. Equivalent to QHostAddress(). + \value LocalHost The IPv4 localhost address. Equivalent to QHostAddress("127.0.0.1"). + \value LocalHostIPv6 The IPv6 localhost address. Equivalent to QHostAddress("::1"). + \value Broadcast The IPv4 broadcast address. Equivalent to QHostAddress("255.255.255.255"). + \value Any The IPv4 any-address. Equivalent to QHostAddress("0.0.0.0"). + \value AnyIPv6 The IPv6 any-address. Equivalent to QHostAddress("::"). +*/ + +/*! Constructs a host address object with the IP address 0.0.0.0. + + \sa clear() +*/ +QHostAddress::QHostAddress() + : d(new QHostAddressPrivate) +{ +} + +/*! + Constructs a host address object with the IPv4 address \a ip4Addr. +*/ +QHostAddress::QHostAddress(quint32 ip4Addr) + : d(new QHostAddressPrivate) +{ + setAddress(ip4Addr); +} + +/*! + Constructs a host address object with the IPv6 address \a ip6Addr. + + \a ip6Addr must be a 16-byte array in network byte order (big + endian). +*/ +QHostAddress::QHostAddress(quint8 *ip6Addr) + : d(new QHostAddressPrivate) +{ + setAddress(ip6Addr); +} + +/*! + Constructs a host address object with the IPv6 address \a ip6Addr. +*/ +QHostAddress::QHostAddress(const Q_IPV6ADDR &ip6Addr) + : d(new QHostAddressPrivate) +{ + setAddress(ip6Addr); +} + +/*! + Constructs an IPv4 or IPv6 address based on the string \a address + (e.g., "127.0.0.1"). + + \sa setAddress() +*/ +QHostAddress::QHostAddress(const QString &address) + : d(new QHostAddressPrivate) +{ + d->ipString = address; + d->isParsed = false; +} + +/*! + \fn QHostAddress::QHostAddress(const sockaddr *sockaddr) + + Constructs an IPv4 or IPv6 address using the address specified by + the native structure \a sockaddr. + + \sa setAddress() +*/ +QHostAddress::QHostAddress(const struct sockaddr *sockaddr) + : d(new QHostAddressPrivate) +{ + if (sockaddr->sa_family == AF_INET) + setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr)); +#ifndef QT_NO_IPV6 + else if (sockaddr->sa_family == AF_INET6) + setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr); +#endif +} + +/*! + Constructs a copy of the given \a address. +*/ +QHostAddress::QHostAddress(const QHostAddress &address) + : d(new QHostAddressPrivate(*address.d)) +{ +} + +/*! + Constructs a QHostAddress object for \a address. +*/ +QHostAddress::QHostAddress(SpecialAddress address) + : d(new QHostAddressPrivate) +{ + switch (address) { + case Null: + break; + case Broadcast: + setAddress(QLatin1String("255.255.255.255")); + break; + case LocalHost: + setAddress(QLatin1String("127.0.0.1")); + break; + case LocalHostIPv6: + setAddress(QLatin1String("::1")); + break; + case Any: + setAddress(QLatin1String("0.0.0.0")); + break; + case AnyIPv6: + setAddress(QLatin1String("::")); + break; + } +} + +/*! + Destroys the host address object. +*/ +QHostAddress::~QHostAddress() +{ + delete d; +} + +/*! + Assigns another host \a address to this object, and returns a reference + to this object. +*/ +QHostAddress &QHostAddress::operator=(const QHostAddress &address) +{ + *d = *address.d; + return *this; +} + +/*! + Assigns the host address \a address to this object, and returns a + reference to this object. + + \sa setAddress() +*/ +QHostAddress &QHostAddress::operator=(const QString &address) +{ + setAddress(address); + return *this; +} + +/*! + \fn bool QHostAddress::operator!=(const QHostAddress &other) const + \since 4.2 + + Returns true if this host address is not the same as the \a other + address given; otherwise returns false. +*/ + +/*! + \fn bool QHostAddress::operator!=(SpecialAddress other) const + + Returns true if this host address is not the same as the \a other + address given; otherwise returns false. +*/ + +/*! + Sets the host address to 0.0.0.0. +*/ +void QHostAddress::clear() +{ + d->clear(); +} + +/*! + Set the IPv4 address specified by \a ip4Addr. +*/ +void QHostAddress::setAddress(quint32 ip4Addr) +{ + d->setAddress(ip4Addr); +} + +/*! + \overload + + Set the IPv6 address specified by \a ip6Addr. + + \a ip6Addr must be an array of 16 bytes in network byte order + (high-order byte first). +*/ +void QHostAddress::setAddress(quint8 *ip6Addr) +{ + d->setAddress(ip6Addr); +} + +/*! + \overload + + Set the IPv6 address specified by \a ip6Addr. +*/ +void QHostAddress::setAddress(const Q_IPV6ADDR &ip6Addr) +{ + d->setAddress(ip6Addr); +} + +/*! + \overload + + Sets the IPv4 or IPv6 address specified by the string + representation specified by \a address (e.g. "127.0.0.1"). + Returns true and sets the address if the address was successfully + parsed; otherwise returns false. +*/ +bool QHostAddress::setAddress(const QString &address) +{ + d->ipString = address; + return d->parse(); +} + +/*! + \fn void QHostAddress::setAddress(const sockaddr *sockaddr) + \overload + + Sets the IPv4 or IPv6 address specified by the native structure \a + sockaddr. Returns true and sets the address if the address was + successfully parsed; otherwise returns false. +*/ +void QHostAddress::setAddress(const struct sockaddr *sockaddr) +{ + clear(); + if (sockaddr->sa_family == AF_INET) + setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr)); +#ifndef QT_NO_IPV6 + else if (sockaddr->sa_family == AF_INET6) + setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr); +#endif +} + +/*! + Returns the IPv4 address as a number. + + For example, if the address is 127.0.0.1, the returned value is + 2130706433 (i.e. 0x7f000001). + + This value is only valid if isIp4Addr() returns true. + + \sa toString() +*/ +quint32 QHostAddress::toIPv4Address() const +{ + QT_ENSURE_PARSED(this); + return d->a; +} + +/*! + Returns the network layer protocol of the host address. +*/ +QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol() const +{ + QT_ENSURE_PARSED(this); + return d->protocol; +} + +/*! + Returns the IPv6 address as a Q_IPV6ADDR structure. The structure + consists of 16 unsigned characters. + + \snippet doc/src/snippets/code/src_network_kernel_qhostaddress.cpp 0 + + This value is only valid if isIPv6Address() returns true. + + \sa toString() +*/ +Q_IPV6ADDR QHostAddress::toIPv6Address() const +{ + QT_ENSURE_PARSED(this); + return d->a6; +} + +/*! + Returns the address as a string. + + For example, if the address is the IPv4 address 127.0.0.1, the + returned string is "127.0.0.1". + + \sa toIPv4Address() +*/ +QString QHostAddress::toString() const +{ + QT_ENSURE_PARSED(this); + if (d->protocol == QAbstractSocket::IPv4Protocol) { + quint32 i = toIPv4Address(); + QString s; + s.sprintf("%d.%d.%d.%d", (i>>24) & 0xff, (i>>16) & 0xff, + (i >> 8) & 0xff, i & 0xff); + return s; + } + + if (d->protocol == QAbstractSocket::IPv6Protocol) { + quint16 ugle[8]; + for (int i = 0; i < 8; i++) { + ugle[i] = (quint16(d->a6[2*i]) << 8) | quint16(d->a6[2*i+1]); + } + QString s; + s.sprintf("%X:%X:%X:%X:%X:%X:%X:%X", + ugle[0], ugle[1], ugle[2], ugle[3], ugle[4], ugle[5], ugle[6], ugle[7]); + if (!d->scopeId.isEmpty()) + s.append(QLatin1Char('%') + d->scopeId); + return s; + } + + return QString(); +} + +/*! + \since 4.1 + + Returns the scope ID of an IPv6 address. For IPv4 addresses, or if the + address does not contain a scope ID, an empty QString is returned. + + The IPv6 scope ID specifies the scope of \e reachability for non-global + IPv6 addresses, limiting the area in which the address can be used. All + IPv6 addresses are associated with such a reachability scope. The scope ID + is used to disambiguate addresses that are not guaranteed to be globally + unique. + + IPv6 specifies the following four levels of reachability: + + \list + + \o Node-local: Addresses that are only used for communicating with + services on the same interface (e.g., the loopback interface "::1"). + + \o Link-local: Addresses that are local to the network interface + (\e{link}). There is always one link-local address for each IPv6 interface + on your host. Link-local addresses ("fe80...") are generated from the MAC + address of the local network adaptor, and are not guaranteed to be unique. + + \o Site-local: Addresses that are local to the site / private network + (e.g., the company intranet). Site-local addresses ("fec0...") are + usually distributed by the site router, and are not guaranteed to be + unique outside of the local site. + + \o Global: For globally routable addresses, such as public servers on the + Internet. + + \endlist + + When using a link-local or site-local address for IPv6 connections, you + must specify the scope ID. The scope ID for a link-local address is + usually the same as the interface name (e.g., "eth0", "en1") or number + (e.g., "1", "2"). + + \sa setScopeId() +*/ +QString QHostAddress::scopeId() const +{ + QT_ENSURE_PARSED(this); + return (d->protocol == QAbstractSocket::IPv6Protocol) ? d->scopeId : QString(); +} + +/*! + \since 4.1 + + Sets the IPv6 scope ID of the address to \a id. If the address + protocol is not IPv6, this function does nothing. +*/ +void QHostAddress::setScopeId(const QString &id) +{ + QT_ENSURE_PARSED(this); + if (d->protocol == QAbstractSocket::IPv6Protocol) + d->scopeId = id; +} + +/*! + Returns true if this host address is the same as the \a other address + given; otherwise returns false. +*/ +bool QHostAddress::operator==(const QHostAddress &other) const +{ + QT_ENSURE_PARSED(this); + QT_ENSURE_PARSED(&other); + + if (d->protocol == QAbstractSocket::IPv4Protocol) + return other.d->protocol == QAbstractSocket::IPv4Protocol && d->a == other.d->a; + if (d->protocol == QAbstractSocket::IPv6Protocol) { + return other.d->protocol == QAbstractSocket::IPv6Protocol + && memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0; + } + return d->protocol == other.d->protocol; +} + +/*! + Returns true if this host address is the same as the \a other + address given; otherwise returns false. +*/ +bool QHostAddress::operator ==(SpecialAddress other) const +{ + QT_ENSURE_PARSED(this); + QHostAddress otherAddress(other); + QT_ENSURE_PARSED(&otherAddress); + + if (d->protocol == QAbstractSocket::IPv4Protocol) + return otherAddress.d->protocol == QAbstractSocket::IPv4Protocol && d->a == otherAddress.d->a; + if (d->protocol == QAbstractSocket::IPv6Protocol) { + return otherAddress.d->protocol == QAbstractSocket::IPv6Protocol + && memcmp(&d->a6, &otherAddress.d->a6, sizeof(Q_IPV6ADDR)) == 0; + } + return int(other) == int(Null); +} + +/*! + Returns true if this host address is null (INADDR_ANY or in6addr_any). + The default constructor creates a null address, and that address is + not valid for any host or interface. +*/ +bool QHostAddress::isNull() const +{ + QT_ENSURE_PARSED(this); + return d->protocol == QAbstractSocket::UnknownNetworkLayerProtocol; +} + +/*! + \fn quint32 QHostAddress::ip4Addr() const + + Use toIPv4Address() instead. +*/ + +/*! + \fn bool QHostAddress::isIp4Addr() const + + Use protocol() instead. +*/ + +/*! + \fn bool QHostAddress::isIPv4Address() const + + Use protocol() instead. +*/ + +/*! + \fn bool QHostAddress::isIPv6Address() const + + Use protocol() instead. +*/ + +/*! + \since 4.5 + + Returns true if this IP is in the subnet described by the network + prefix \a subnet and netmask \a netmask. + + An IP is considered to belong to a subnet if it is contained + between the lowest and the highest address in that subnet. In the + case of IP version 4, the lowest address is the network address, + while the highest address is the broadcast address. + + The \a subnet argument does not have to be the actual network + address (the lowest address in the subnet). It can be any valid IP + belonging to that subnet. In particular, if it is equal to the IP + address held by this object, this function will always return true + (provided the netmask is a valid value). + + \sa parseSubnet() +*/ +bool QHostAddress::isInSubnet(const QHostAddress &subnet, int netmask) const +{ + QT_ENSURE_PARSED(this); + if (subnet.protocol() != d->protocol || netmask < 0) + return false; + + union { + quint32 ip; + quint8 data[4]; + } ip4, net4; + const quint8 *ip; + const quint8 *net; + if (d->protocol == QAbstractSocket::IPv4Protocol) { + if (netmask > 32) + netmask = 32; + ip4.ip = qToBigEndian(d->a); + net4.ip = qToBigEndian(subnet.d->a); + ip = ip4.data; + net = net4.data; + } else if (d->protocol == QAbstractSocket::IPv6Protocol) { + if (netmask > 128) + netmask = 128; + ip = d->a6.c; + net = subnet.d->a6.c; + } else { + return false; + } + + if (netmask >= 8 && memcmp(ip, net, netmask / 8) != 0) + return false; + if ((netmask & 7) == 0) + return true; + + // compare the last octet now + quint8 bytemask = 256 - (1 << (8 - (netmask & 7))); + quint8 ipbyte = ip[netmask / 8]; + quint8 netbyte = net[netmask / 8]; + return (ipbyte & bytemask) == (netbyte & bytemask); +} + +/*! + \since 4.5 + \overload + + Returns true if this IP is in the subnet described by \a + subnet. The QHostAddress member of \a subnet contains the network + prefix and the int (second) member contains the netmask (prefix + length). +*/ +bool QHostAddress::isInSubnet(const QPair<QHostAddress, int> &subnet) const +{ + return isInSubnet(subnet.first, subnet.second); +} + + +/*! + \since 4.5 + + Parses the IP and subnet information contained in \a subnet and + returns the network prefix for that network and its prefix length. + + The IP address and the netmask must be separated by a slash + (/). + + This function supports arguments in the form: + \list + \o 123.123.123.123/n where n is any value between 0 and 32 + \o 123.123.123.123/255.255.255.255 + \o <ipv6-address>/n where n is any value between 0 and 128 + \endlist + + For IP version 4, this function accepts as well missing trailing + components (i.e., less than 4 octets, like "192.168.1"), followed + or not by a dot. If the netmask is also missing in that case, it + is set to the number of octets actually passed (in the example + above, it would be 24, for 3 octets). + + \sa isInSubnet() +*/ +QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet) +{ + // We support subnets in the form: + // ddd.ddd.ddd.ddd/nn + // ddd.ddd.ddd/nn + // ddd.ddd/nn + // ddd/nn + // ddd.ddd.ddd. + // ddd.ddd.ddd + // ddd.ddd. + // ddd.ddd + // ddd. + // ddd + // <ipv6-address>/nn + // + // where nn can be an IPv4-style netmask for the IPv4 forms + + const QPair<QHostAddress, int> invalid = qMakePair(QHostAddress(), -1); + if (subnet.isEmpty()) + return invalid; + + int slash = subnet.indexOf(QLatin1Char('/')); + QString netStr = subnet; + if (slash != -1) + netStr.truncate(slash); + + int netmask = -1; + bool isIpv6 = netStr.contains(QLatin1Char(':')); + + if (slash != -1) { + // is the netmask given in IP-form or in bit-count form? + if (!isIpv6 && subnet.indexOf(QLatin1Char('.'), slash + 1) != -1) { + // IP-style, convert it to bit-count form + QNetmaskAddress parser; + if (!parser.setAddress(subnet.mid(slash + 1))) + return invalid; + netmask = parser.prefixLength(); + } else { + bool ok; + netmask = subnet.mid(slash + 1).toUInt(&ok); + if (!ok) + return invalid; // failed to parse the subnet + } + } + + if (isIpv6) { + // looks like it's an IPv6 address + if (netmask > 128) + return invalid; // invalid netmask + if (netmask < 0) + netmask = 128; + + QHostAddress net; + if (!net.setAddress(netStr)) + return invalid; // failed to parse the IP + + clearBits(net.d->a6.c, netmask, 128); + return qMakePair(net, netmask); + } + + if (netmask > 32) + return invalid; // invalid netmask + + // parse the address manually + QStringList parts = netStr.split(QLatin1Char('.')); + if (parts.isEmpty() || parts.count() > 4) + return invalid; // invalid IPv4 address + + if (parts.last().isEmpty()) + parts.removeLast(); + + quint32 addr = 0; + for (int i = 0; i < parts.count(); ++i) { + bool ok; + uint byteValue = parts.at(i).toUInt(&ok); + if (!ok || byteValue > 255) + return invalid; // invalid IPv4 address + + addr <<= 8; + addr += byteValue; + } + addr <<= 8 * (4 - parts.count()); + if (netmask == -1) { + netmask = 8 * parts.count(); + } else if (netmask == 0) { + // special case here + // x86's instructions "shr" and "shl" do not operate when + // their argument is 32, so the code below doesn't work as expected + addr = 0; + } else if (netmask != 32) { + // clear remaining bits + quint32 mask = quint32(0xffffffff) >> (32 - netmask) << (32 - netmask); + addr &= mask; + } + + return qMakePair(QHostAddress(addr), netmask); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const QHostAddress &address) +{ + d.maybeSpace() << "QHostAddress(" << address.toString() << ")"; + return d.space(); +} +#endif + +uint qHash(const QHostAddress &key) +{ + return qHash(key.toString()); +} + +#ifndef QT_NO_DATASTREAM + +/*! \relates QHostAddress + + Writes host address \a address to the stream \a out and returns a reference + to the stream. + + \sa {Format of the QDataStream operators} +*/ +QDataStream &operator<<(QDataStream &out, const QHostAddress &address) +{ + qint8 prot; + prot = qint8(address.protocol()); + out << prot; + switch (address.protocol()) { + case QAbstractSocket::UnknownNetworkLayerProtocol: + break; + case QAbstractSocket::IPv4Protocol: + out << address.toIPv4Address(); + break; + case QAbstractSocket::IPv6Protocol: + { + Q_IPV6ADDR ipv6 = address.toIPv6Address(); + for (int i = 0; i < 16; ++i) + out << ipv6[i]; + out << address.scopeId(); + } + break; + } + return out; +} + +/*! \relates QHostAddress + + Reads a host address into \a address from the stream \a in and returns a + reference to the stream. + + \sa {Format of the QDataStream operators} +*/ +QDataStream &operator>>(QDataStream &in, QHostAddress &address) +{ + qint8 prot; + in >> prot; + switch (QAbstractSocket::NetworkLayerProtocol(prot)) { + case QAbstractSocket::UnknownNetworkLayerProtocol: + address.clear(); + break; + case QAbstractSocket::IPv4Protocol: + { + quint32 ipv4; + in >> ipv4; + address.setAddress(ipv4); + } + break; + case QAbstractSocket::IPv6Protocol: + { + Q_IPV6ADDR ipv6; + for (int i = 0; i < 16; ++i) + in >> ipv6[i]; + address.setAddress(ipv6); + + QString scope; + in >> scope; + address.setScopeId(scope); + } + break; + default: + address.clear(); + in.setStatus(QDataStream::ReadCorruptData); + } + return in; +} + +#endif //QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h new file mode 100644 index 0000000..02a6f97 --- /dev/null +++ b/src/network/kernel/qhostaddress.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHOSTADDRESS_H +#define QHOSTADDRESS_H + +#include <QtCore/qpair.h> +#include <QtCore/qstring.h> +#include <QtNetwork/qabstractsocket.h> + +struct sockaddr; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +class QHostAddressPrivate; + +class Q_NETWORK_EXPORT QIPv6Address +{ +public: + inline quint8 &operator [](int index) { return c[index]; } + inline quint8 operator [](int index) const { return c[index]; } + quint8 c[16]; +}; + +typedef QIPv6Address Q_IPV6ADDR; + +class Q_NETWORK_EXPORT QHostAddress +{ +public: + enum SpecialAddress { + Null, + Broadcast, + LocalHost, + LocalHostIPv6, + Any, + AnyIPv6 + }; + + QHostAddress(); + explicit QHostAddress(quint32 ip4Addr); + explicit QHostAddress(quint8 *ip6Addr); + explicit QHostAddress(const Q_IPV6ADDR &ip6Addr); + explicit QHostAddress(const sockaddr *sockaddr); + explicit QHostAddress(const QString &address); + QHostAddress(const QHostAddress ©); + QHostAddress(SpecialAddress address); + ~QHostAddress(); + + QHostAddress &operator=(const QHostAddress &other); + QHostAddress &operator=(const QString &address); + + void setAddress(quint32 ip4Addr); + void setAddress(quint8 *ip6Addr); + void setAddress(const Q_IPV6ADDR &ip6Addr); + void setAddress(const sockaddr *sockaddr); + bool setAddress(const QString &address); + + QAbstractSocket::NetworkLayerProtocol protocol() const; + quint32 toIPv4Address() const; + Q_IPV6ADDR toIPv6Address() const; + + QString toString() const; + + QString scopeId() const; + void setScopeId(const QString &id); + + bool operator ==(const QHostAddress &address) const; + bool operator ==(SpecialAddress address) const; + inline bool operator !=(const QHostAddress &address) const + { return !operator==(address); } + inline bool operator !=(SpecialAddress address) const + { return !operator==(address); } + bool isNull() const; + void clear(); + +#ifdef QT3_SUPPORT + inline QT3_SUPPORT quint32 ip4Addr() const { return toIPv4Address(); } + inline QT3_SUPPORT bool isIPv4Address() const { return protocol() == QAbstractSocket::IPv4Protocol + || protocol() == QAbstractSocket::UnknownNetworkLayerProtocol; } + inline QT3_SUPPORT bool isIp4Addr() const { return protocol() == QAbstractSocket::IPv4Protocol + || protocol() == QAbstractSocket::UnknownNetworkLayerProtocol; } + inline QT3_SUPPORT bool isIPv6Address() const { return protocol() == QAbstractSocket::IPv6Protocol; } +#endif + + bool isInSubnet(const QHostAddress &subnet, int netmask) const; + bool isInSubnet(const QPair<QHostAddress, int> &subnet) const; + + static QPair<QHostAddress, int> parseSubnet(const QString &subnet); + +protected: + QHostAddressPrivate *d; +}; + +inline bool operator ==(QHostAddress::SpecialAddress address1, const QHostAddress &address2) +{ return address2 == address1; } + +#ifndef QT_NO_DEBUG_STREAM +Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QHostAddress &); +#endif + + +Q_NETWORK_EXPORT uint qHash(const QHostAddress &key); + +#ifndef QT_NO_DATASTREAM +Q_NETWORK_EXPORT QDataStream &operator<<(QDataStream &, const QHostAddress &); +Q_NETWORK_EXPORT QDataStream &operator>>(QDataStream &, QHostAddress &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QHOSTADDRESS_H diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h new file mode 100644 index 0000000..e6634b7 --- /dev/null +++ b/src/network/kernel/qhostaddress_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHOSTADDRESSPRIVATE_H +#define QHOSTADDRESSPRIVATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QHostAddress and QNetworkInterface classes. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +#include "qhostaddress.h" +#include "qabstractsocket.h" + +class QNetmaskAddress: public QHostAddress +{ + int length; +public: + QNetmaskAddress() : QHostAddress(), length(-1) { } + + bool setAddress(const QString &address); + bool setAddress(const QHostAddress &address); + + int prefixLength() const; + void setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int len); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp new file mode 100644 index 0000000..ca4124d --- /dev/null +++ b/src/network/kernel/qhostinfo.cpp @@ -0,0 +1,479 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhostinfo.h" +#include "qhostinfo_p.h" + +#include <qabstracteventdispatcher.h> +#include <private/qunicodetables_p.h> +#include <qcoreapplication.h> +#include <qmetaobject.h> +#include <qregexp.h> +#include <private/qnativesocketengine_p.h> +#include <qstringlist.h> +#include <qthread.h> +#include <qtimer.h> +#include <qurl.h> + +#ifdef Q_OS_UNIX +# include <unistd.h> +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QHostInfoAgent, theAgent) +void QHostInfoAgent::staticCleanup() +{ + theAgent()->cleanup(); +} + +//#define QHOSTINFO_DEBUG + +/*! + \class QHostInfo + \brief The QHostInfo class provides static functions for host name lookups. + + \reentrant + \inmodule QtNetwork + \ingroup io + + QHostInfo uses the lookup mechanisms provided by the operating + system to find the IP address(es) associated with a host name, + or the host name associated with an IP address. + The class provides two static convenience functions: one that + works asynchronously and emits a signal once the host is found, + and one that blocks and returns a QHostInfo object. + + To look up a host's IP addresses asynchronously, call lookupHost(), + which takes the host name or IP address, a receiver object, and a slot + signature as arguments and returns an ID. You can abort the + lookup by calling abortHostLookup() with the lookup ID. + + Example: + + \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0 + + + The slot is invoked when the results are ready. (If you use + Qt for Embedded Linux and disabled multithreading support by defining + \c QT_NO_THREAD, lookupHost() will block until the lookup has + finished.) The results are stored in a QHostInfo object. Call + addresses() to get the list of IP addresses for the host, and + hostName() to get the host name that was looked up. + + If the lookup failed, error() returns the type of error that + occurred. errorString() gives a human-readable description of the + lookup error. + + If you want a blocking lookup, use the QHostInfo::fromName() function: + + \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 1 + + QHostInfo supports Internationalized Domain Names (IDNs) through the + IDNA and Punycode standards. + + To retrieve the name of the local host, use the static + QHostInfo::localHostName() function. + + \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492} +*/ + +static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1); + +/*! + Looks up the IP address(es) associated with host name \a name, and + returns an ID for the lookup. When the result of the lookup is + ready, the slot or signal \a member in \a receiver is called with + a QHostInfo argument. The QHostInfo object can then be inspected + to get the results of the lookup. + + The lookup is performed by a single function call, for example: + + \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 2 + + The implementation of the slot prints basic information about the + addresses returned by the lookup, or reports an error if it failed: + + \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 3 + + If you pass a literal IP address to \a name instead of a host name, + QHostInfo will search for the domain name for the IP (i.e., QHostInfo will + perform a \e reverse lookup). On success, the resulting QHostInfo will + contain both the resolved domain name and IP addresses for the host + name. Example: + + \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4 + + \sa abortHostLookup(), addresses(), error(), fromName() +*/ +int QHostInfo::lookupHost(const QString &name, QObject *receiver, + const char *member) +{ +#if defined QHOSTINFO_DEBUG + qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)", + name.toLatin1().constData(), receiver, member ? member + 1 : 0); +#endif + if (!QAbstractEventDispatcher::instance(QThread::currentThread())) { + qWarning("QHostInfo::lookupHost() called with no event dispatcher"); + return -1; + } + + qRegisterMetaType<QHostInfo>("QHostInfo"); + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + QWindowsSockInit bust; // makes sure WSAStartup was callled +#endif + + // Support for IDNA + QString lookup = QString::fromLatin1(QUrl::toAce(name)); + + QHostInfoResult *result = new QHostInfoResult; + result->autoDelete = false; + QObject::connect(result, SIGNAL(resultsReady(QHostInfo)), + receiver, member); + int id = result->lookupId = theIdCounter.fetchAndAddRelaxed(1); + + if (lookup.isEmpty()) { + QHostInfo info(id); + info.setError(QHostInfo::HostNotFound); + info.setErrorString(QObject::tr("No host name given")); + QMetaObject::invokeMethod(result, "emitResultsReady", Qt::QueuedConnection, + Q_ARG(QHostInfo, info)); + result->autoDelete = true; + return id; + } + + QHostInfoAgent *agent = theAgent(); + agent->addHostName(lookup, result); + +#if !defined QT_NO_THREAD + if (!agent->isRunning()) + agent->start(); +#else +// if (!agent->isRunning()) + agent->run(); +// else +// agent->wakeOne(); +#endif + return id; +} + +/*! + Aborts the host lookup with the ID \a id, as returned by lookupHost(). + + \sa lookupHost(), lookupId() +*/ +void QHostInfo::abortHostLookup(int id) +{ + QHostInfoAgent *agent = theAgent(); + agent->abortLookup(id); +} + +/*! + Looks up the IP address(es) for the given host \a name. The + function blocks during the lookup which means that execution of + the program is suspended until the results of the lookup are + ready. Returns the result of the lookup in a QHostInfo object. + + If you pass a literal IP address to \a name instead of a host name, + QHostInfo will search for the domain name for the IP (i.e., QHostInfo will + perform a \e reverse lookup). On success, the returned QHostInfo will + contain both the resolved domain name and IP addresses for the host name. + + \sa lookupHost() +*/ +QHostInfo QHostInfo::fromName(const QString &name) +{ +#if defined QHOSTINFO_DEBUG + qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData()); +#endif + + if (!name.isEmpty()) + return QHostInfoAgent::fromName(QLatin1String(QUrl::toAce(name))); + + QHostInfo retval; + retval.d->err = HostNotFound; + retval.d->errorStr = QObject::tr("No host name given"); + return retval; +} + +/*! + \internal + Pops a query off the queries list, performs a blocking call to + QHostInfoAgent::lookupHost(), and emits the resultsReady() + signal. This process repeats until the queries list is empty. +*/ +void QHostInfoAgent::run() +{ +#ifndef QT_NO_THREAD + // Dont' allow thread termination during event delivery, but allow it + // during the actual blocking host lookup stage. + setTerminationEnabled(false); + forever +#endif + { + QHostInfoQuery *query; + { +#ifndef QT_NO_THREAD + // the queries list is shared between threads. lock all + // access to it. + QMutexLocker locker(&mutex); + if (!quit && queries.isEmpty()) + cond.wait(&mutex); + if (quit) { + // Reset the quit variable in case QCoreApplication is + // destroyed and recreated. + quit = false; + break; + } + if (queries.isEmpty()) + continue; +#else + if (queries.isEmpty()) + return; +#endif + query = queries.takeFirst(); + pendingQueryId = query->object->lookupId; + } + +#if defined(QHOSTINFO_DEBUG) + qDebug("QHostInfoAgent::run(%p): looking up \"%s\"", this, + query->hostName.toLatin1().constData()); +#endif + +#ifndef QT_NO_THREAD + // Start query - allow termination at this point, but not outside. We + // don't want to all termination during event delivery, but we don't + // want the lookup to prevent the app from quitting (the agent + // destructor terminates the thread). + setTerminationEnabled(true); +#endif + QHostInfo info = fromName(query->hostName); +#ifndef QT_NO_THREAD + setTerminationEnabled(false); +#endif + + int id = query->object->lookupId; + info.setLookupId(id); + if (pendingQueryId == id) + query->object->emitResultsReady(info); + delete query; + } +} + +/*! + \enum QHostInfo::HostInfoError + + This enum describes the various errors that can occur when trying + to resolve a host name. + + \value NoError The lookup was successful. + \value HostNotFound No IP addresses were found for the host. + \value UnknownError An unknown error occurred. + + \sa error(), setError() +*/ + +/*! + Constructs an empty host info object with lookup ID \a id. + + \sa lookupId() +*/ +QHostInfo::QHostInfo(int id) + : d(new QHostInfoPrivate) +{ + d->lookupId = id; +} + +/*! + Constructs a copy of \a other. +*/ +QHostInfo::QHostInfo(const QHostInfo &other) + : d(new QHostInfoPrivate(*other.d)) +{ +} + +/*! + Assigns the data of the \a other object to this host info object, + and returns a reference to it. +*/ +QHostInfo &QHostInfo::operator=(const QHostInfo &other) +{ + *d = *other.d; + return *this; +} + +/*! + Destroys the host info object. +*/ +QHostInfo::~QHostInfo() +{ + delete d; +} + +/*! + Returns the list of IP addresses associated with hostName(). This + list may be empty. + + Example: + + \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 5 + + \sa hostName(), error() +*/ +QList<QHostAddress> QHostInfo::addresses() const +{ + return d->addrs; +} + +/*! + Sets the list of addresses in this QHostInfo to \a addresses. + + \sa addresses() +*/ +void QHostInfo::setAddresses(const QList<QHostAddress> &addresses) +{ + d->addrs = addresses; +} + +/*! + Returns the name of the host whose IP addresses were looked up. + + \sa localHostName() +*/ +QString QHostInfo::hostName() const +{ + return d->hostName; +} + +/*! + Sets the host name of this QHostInfo to \a hostName. + + \sa hostName() +*/ +void QHostInfo::setHostName(const QString &hostName) +{ + d->hostName = hostName; +} + +/*! + Returns the type of error that occurred if the host name lookup + failed; otherwise returns NoError. + + \sa setError(), errorString() +*/ +QHostInfo::HostInfoError QHostInfo::error() const +{ + return d->err; +} + +/*! + Sets the error type of this QHostInfo to \a error. + + \sa error(), errorString() +*/ +void QHostInfo::setError(HostInfoError error) +{ + d->err = error; +} + +/*! + Returns the ID of this lookup. + + \sa setLookupId(), abortHostLookup(), hostName() +*/ +int QHostInfo::lookupId() const +{ + return d->lookupId; +} + +/*! + Sets the ID of this lookup to \a id. + + \sa lookupId(), lookupHost() +*/ +void QHostInfo::setLookupId(int id) +{ + d->lookupId = id; +} + +/*! + If the lookup failed, this function returns a human readable + description of the error; otherwise "Unknown error" is returned. + + \sa setErrorString(), error() +*/ +QString QHostInfo::errorString() const +{ + return d->errorStr; +} + +/*! + Sets the human readable description of the error that occurred to \a str + if the lookup failed. + + \sa errorString(), setError() +*/ +void QHostInfo::setErrorString(const QString &str) +{ + d->errorStr = str; +} + +/*! + \fn QString QHostInfo::localHostName() + + Returns the host name of this machine. + + \sa hostName() +*/ + +/*! + \fn QString QHostInfo::localDomainName() + + Returns the DNS domain of this machine. + + Note: DNS domains are not related to domain names found in + Windows networks. + + \sa hostName() +*/ + +QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h new file mode 100644 index 0000000..084ec89 --- /dev/null +++ b/src/network/kernel/qhostinfo.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHOSTINFO_H +#define QHOSTINFO_H + +#include <QtCore/qlist.h> +#include <QtNetwork/qhostaddress.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +class QObject; +class QHostInfoPrivate; + +class Q_NETWORK_EXPORT QHostInfo +{ +public: + enum HostInfoError { + NoError, + HostNotFound, + UnknownError + }; + + QHostInfo(int lookupId = -1); + QHostInfo(const QHostInfo &d); + QHostInfo &operator=(const QHostInfo &d); + ~QHostInfo(); + + QString hostName() const; + void setHostName(const QString &name); + + QList<QHostAddress> addresses() const; + void setAddresses(const QList<QHostAddress> &addresses); + + HostInfoError error() const; + void setError(HostInfoError error); + + QString errorString() const; + void setErrorString(const QString &errorString); + + void setLookupId(int id); + int lookupId() const; + + static int lookupHost(const QString &name, QObject *receiver, const char *member); + static void abortHostLookup(int lookupId); + + static QHostInfo fromName(const QString &name); + static QString localHostName(); + static QString localDomainName(); + +private: + QHostInfoPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QHOSTINFO_H diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h new file mode 100644 index 0000000..1db00d3 --- /dev/null +++ b/src/network/kernel/qhostinfo_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHOSTINFO_P_H +#define QHOSTINFO_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qcoreapplication.h" +#include "private/qcoreapplication_p.h" +#include "QtNetwork/qhostinfo.h" +#include "QtCore/qmutex.h" +#include "QtCore/qwaitcondition.h" +#include "QtCore/qobject.h" +#include "QtCore/qpointer.h" + +#if !defined QT_NO_THREAD +#include "QtCore/qthread.h" +# define QHostInfoAgentBase QThread +#else +# define QHostInfoAgentBase QObject +#endif + +QT_BEGIN_NAMESPACE + +static const int QHOSTINFO_THREAD_WAIT = 250; // ms + +class QHostInfoResult : public QObject +{ + Q_OBJECT +public Q_SLOTS: + inline void emitResultsReady(const QHostInfo &info) + { + emit resultsReady(info); + if (autoDelete) + delete this; + } + +Q_SIGNALS: + void resultsReady(const QHostInfo &info); + +public: + int lookupId; + bool autoDelete; +}; + +struct QHostInfoQuery +{ + inline QHostInfoQuery() : object(0) {} + inline ~QHostInfoQuery() { delete object; } + inline QHostInfoQuery(const QString &name, QHostInfoResult *result) + : hostName(name), object(result) {} + + QString hostName; + QHostInfoResult *object; +}; + +class QHostInfoAgent : public QHostInfoAgentBase +{ + Q_OBJECT +public: + inline QHostInfoAgent() + { + // There is a chance that there will be two instances of + // QHostInfoAgent if two threads try to get Q_GLOBAL_STATIC + // object at the same time. The second object will be deleted + // immediately before anyone uses it, but we need to be + // careful about what we do in the constructor. + static QBasicAtomicInt done = Q_BASIC_ATOMIC_INITIALIZER(0); + if (done.testAndSetRelaxed(0, 1)) + qAddPostRoutine(staticCleanup); + moveToThread(QCoreApplicationPrivate::mainThread()); + quit = false; + pendingQueryId = -1; + } + inline ~QHostInfoAgent() + { cleanup(); } + + void run(); + static QHostInfo fromName(const QString &hostName); + + inline void addHostName(const QString &name, QHostInfoResult *result) + { + QMutexLocker locker(&mutex); + queries << new QHostInfoQuery(name, result); + cond.wakeOne(); + } + + inline void abortLookup(int id) + { + QMutexLocker locker(&mutex); + for (int i = 0; i < queries.size(); ++i) { + QHostInfoResult *result = queries.at(i)->object; + if (result->lookupId == id) { + result->disconnect(); + delete queries.takeAt(i); + return; + } + } + if (pendingQueryId == id) + pendingQueryId = -1; + } + + static void staticCleanup(); + +public Q_SLOTS: + inline void cleanup() + { + { + QMutexLocker locker(&mutex); + qDeleteAll(queries); + queries.clear(); + quit = true; + cond.wakeOne(); + } +#ifndef QT_NO_THREAD + if (!wait(QHOSTINFO_THREAD_WAIT)) + terminate(); + wait(); +#endif + } + +private: + QList<QHostInfoQuery *> queries; + QMutex mutex; + QWaitCondition cond; + volatile bool quit; + int pendingQueryId; +}; + +class QHostInfoPrivate +{ +public: + inline QHostInfoPrivate() + : err(QHostInfo::NoError), + errorStr(QLatin1String(QT_TRANSLATE_NOOP("QHostInfo", "Unknown error"))) + { + } + + QHostInfo::HostInfoError err; + QString errorStr; + QList<QHostAddress> addrs; + QString hostName; + int lookupId; +}; + +QT_END_NAMESPACE + +#endif // QHOSTINFO_P_H diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp new file mode 100644 index 0000000..f987520 --- /dev/null +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QHOSTINFO_DEBUG + +static const int RESOLVER_TIMEOUT = 2000; + +#include "qplatformdefs.h" + +#include "qhostinfo_p.h" +#include "qiodevice.h" +#include <qbytearray.h> +#include <qlibrary.h> +#include <qurl.h> +#include <qfile.h> +#include <private/qmutexpool_p.h> + +extern "C" { +#include <sys/types.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <resolv.h> +} + +#if defined (QT_NO_GETADDRINFO) +#include <qmutex.h> +QT_BEGIN_NAMESPACE +Q_GLOBAL_STATIC(QMutex, getHostByNameMutex) +QT_END_NAMESPACE +#endif + +QT_BEGIN_NAMESPACE + +// Almost always the same. If not, specify in qplatformdefs.h. +#if !defined(QT_SOCKOPTLEN_T) +# define QT_SOCKOPTLEN_T QT_SOCKLEN_T +#endif + +// HP-UXi has a bug in getaddrinfo(3) that makes it thread-unsafe +// with this flag. So disable it in that platform. +#if defined(AI_ADDRCONFIG) && !defined(Q_OS_HPUX) +# define Q_ADDRCONFIG AI_ADDRCONFIG +#endif + +typedef struct __res_state *res_state_ptr; + +typedef int (*res_init_proto)(void); +static res_init_proto local_res_init = 0; +typedef int (*res_ninit_proto)(res_state_ptr); +static res_ninit_proto local_res_ninit = 0; +typedef void (*res_nclose_proto)(res_state_ptr); +static res_nclose_proto local_res_nclose = 0; +static res_state_ptr local_res = 0; + +static void resolveLibrary() +{ +#ifndef QT_NO_LIBRARY + QLibrary lib(QLatin1String("resolv")); + if (!lib.load()) + return; + + local_res_init = res_init_proto(lib.resolve("__res_init")); + if (!local_res_init) + local_res_init = res_init_proto(lib.resolve("res_init")); + + local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit")); + if (!local_res_ninit) + local_res_ninit = res_ninit_proto(lib.resolve("res_ninit")); + + if (!local_res_ninit) { + // if we can't get a thread-safe context, we have to use the global _res state + local_res = res_state_ptr(lib.resolve("_res")); + } else { + local_res_nclose = res_nclose_proto(lib.resolve("res_nclose")); + if (!local_res_nclose) + local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose")); + if (!local_res_nclose) + local_res_ninit = 0; + } +#endif +} + +QHostInfo QHostInfoAgent::fromName(const QString &hostName) +{ + QHostInfo results; + results.setHostName(hostName); + +#if defined(QHOSTINFO_DEBUG) + qDebug("QHostInfoAgent::fromName(%s) looking up...", + hostName.toLatin1().constData()); +#endif + + // Load res_init on demand. + static volatile bool triedResolve = false; + if (!triedResolve) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_init)); +#endif + if (!triedResolve) { + resolveLibrary(); + triedResolve = true; + } + } + + // If res_init is available, poll it. + if (local_res_init) + local_res_init(); + + QHostAddress address; + if (address.setAddress(hostName)) { + // Reverse lookup +// Reverse lookups using getnameinfo are broken on darwin, use gethostbyaddr instead. +#if !defined (QT_NO_GETADDRINFO) && !defined (Q_OS_DARWIN) + sockaddr_in sa4; +#ifndef QT_NO_IPV6 + sockaddr_in6 sa6; +#endif + sockaddr *sa = 0; + QT_SOCKLEN_T saSize = 0; + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + sa = (sockaddr *)&sa4; + saSize = sizeof(sa4); + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + sa4.sin_addr.s_addr = htonl(address.toIPv4Address()); + } +#ifndef QT_NO_IPV6 + else { + sa = (sockaddr *)&sa6; + saSize = sizeof(sa6); + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + memcpy(sa6.sin6_addr.s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.s6_addr)); + } +#endif + + char hbuf[NI_MAXHOST]; + if (!sa || getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) != 0) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + results.setHostName(QString::fromLatin1(hbuf)); +#else + in_addr_t inetaddr = inet_addr(hostName.toLatin1().constData()); + struct hostent *ent = gethostbyaddr((const char *)&inetaddr, sizeof(inetaddr), AF_INET); + if (!ent) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + results.setHostName(QString::fromLatin1(ent->h_name)); +#endif + } + +#if !defined (QT_NO_GETADDRINFO) + // Call getaddrinfo, and place all IPv4 addresses at the start and + // the IPv6 addresses at the end of the address list in results. + addrinfo *res = 0; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; +#ifdef Q_ADDRCONFIG + hints.ai_flags = Q_ADDRCONFIG; +#endif + + int result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res); +# ifdef Q_ADDRCONFIG + if (result == EAI_BADFLAGS) { + // if the lookup failed with AI_ADDRCONFIG set, try again without it + hints.ai_flags = 0; + result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res); + } +# endif + + if (result == 0) { + addrinfo *node = res; + QList<QHostAddress> addresses; + while (node) { + if (node->ai_family == AF_INET) { + QHostAddress addr; + addr.setAddress(ntohl(((sockaddr_in *) node->ai_addr)->sin_addr.s_addr)); + if (!addresses.contains(addr)) + addresses.append(addr); + } +#ifndef QT_NO_IPV6 + else if (node->ai_family == AF_INET6) { + QHostAddress addr; + addr.setAddress(((sockaddr_in6 *) node->ai_addr)->sin6_addr.s6_addr); + if (!addresses.contains(addr)) + addresses.append(addr); + } +#endif + node = node->ai_next; + } + if (addresses.isEmpty() && node == 0) { + // Reached the end of the list, but no addresses were found; this + // means the list contains one or more unknown address types. + results.setError(QHostInfo::UnknownError); + results.setErrorString(tr("Unknown address type")); + } + + results.setAddresses(addresses); + freeaddrinfo(res); + } else if (result == EAI_NONAME + || result == EAI_FAIL +#ifdef EAI_NODATA + // EAI_NODATA is deprecated in RFC 3493 + || result == EAI_NODATA +#endif + ) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + } else { + results.setError(QHostInfo::UnknownError); + results.setErrorString(QString::fromLocal8Bit(gai_strerror(result))); + } + +#else + // Fall back to gethostbyname for platforms that don't define + // getaddrinfo. gethostbyname does not support IPv6, and it's not + // reentrant on all platforms. For now this is okay since we only + // use one QHostInfoAgent, but if more agents are introduced, locking + // must be provided. + QMutexLocker locker(::getHostByNameMutex()); + hostent *result = gethostbyname(hostName.toLatin1().constData()); + if (result) { + if (result->h_addrtype == AF_INET) { + QList<QHostAddress> addresses; + for (char **p = result->h_addr_list; *p != 0; p++) { + QHostAddress addr; + addr.setAddress(ntohl(*((quint32 *)*p))); + if (!addresses.contains(addr)) + addresses.prepend(addr); + } + results.setAddresses(addresses); + } else { + results.setError(QHostInfo::UnknownError); + results.setErrorString(tr("Unknown address type")); + } + } else if (h_errno == HOST_NOT_FOUND || h_errno == NO_DATA + || h_errno == NO_ADDRESS) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + } else { + results.setError(QHostInfo::UnknownError); + results.setErrorString(tr("Unknown error")); + } +#endif // !defined (QT_NO_GETADDRINFO) + +#if defined(QHOSTINFO_DEBUG) + if (results.error() != QHostInfo::NoError) { + qDebug("QHostInfoAgent::fromName(): error #%d %s", + h_errno, results.errorString().toLatin1().constData()); + } else { + QString tmp; + QList<QHostAddress> addresses = results.addresses(); + for (int i = 0; i < addresses.count(); ++i) { + if (i != 0) tmp += ", "; + tmp += addresses.at(i).toString(); + } + qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}", + addresses.count(), hostName.toLatin1().constData(), + tmp.toLatin1().constData()); + } +#endif + return results; +} + +QString QHostInfo::localHostName() +{ + char hostName[512]; + if (gethostname(hostName, sizeof(hostName)) == -1) + return QString(); + hostName[sizeof(hostName) - 1] = '\0'; + return QString::fromLocal8Bit(hostName); +} + +QString QHostInfo::localDomainName() +{ + resolveLibrary(); + if (local_res_ninit) { + // using thread-safe version + res_state_ptr state = res_state_ptr(qMalloc(sizeof(*state))); + memset(state, 0, sizeof(*state)); + local_res_ninit(state); + QString domainName = QUrl::fromAce(state->defdname); + if (domainName.isEmpty()) + domainName = QUrl::fromAce(state->dnsrch[0]); + local_res_nclose(state); + qFree(state); + + return domainName; + } + + if (local_res_init && local_res) { + // using thread-unsafe version + +#if defined(QT_NO_GETADDRINFO) + // We have to call res_init to be sure that _res was initialized + // So, for systems without getaddrinfo (which is thread-safe), we lock the mutex too + QMutexLocker locker(::getHostByNameMutex()); +#endif + local_res_init(); + QString domainName = QUrl::fromAce(local_res->defdname); + if (domainName.isEmpty()) + domainName = QUrl::fromAce(local_res->dnsrch[0]); + return domainName; + } + + // nothing worked, try doing it by ourselves: + QFile resolvconf; +#if defined(_PATH_RESCONF) + resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF)); +#else + resolvconf.setFileName(QLatin1String("/etc/resolv.conf")); +#endif + if (!resolvconf.open(QIODevice::ReadOnly)) + return QString(); // failure + + QString domainName; + while (!resolvconf.atEnd()) { + QByteArray line = resolvconf.readLine().trimmed(); + if (line.startsWith("domain ")) + return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed()); + + // in case there's no "domain" line, fall back to the first "search" entry + if (domainName.isEmpty() && line.startsWith("search ")) { + QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed(); + int pos = searchDomain.indexOf(' '); + if (pos != -1) + searchDomain.truncate(pos); + domainName = QUrl::fromAce(searchDomain); + } + } + + // return the fallen-back-to searched domain + return domainName; +} + +QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp new file mode 100644 index 0000000..0a34e2b --- /dev/null +++ b/src/network/kernel/qhostinfo_win.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined Q_CC_MSVC && _MSC_VER <=1300 +//VC.net 2002 support for templates doesn't match some PSDK requirements +#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) +#endif + +#include <winsock2.h> + +#include "qhostinfo_p.h" +#include "private/qnativesocketengine_p.h" +#include <ws2tcpip.h> +#include <qlibrary.h> +#include <qtimer.h> +#include <qmutex.h> +#include <private/qmutexpool_p.h> + +QT_BEGIN_NAMESPACE + +//#define QHOSTINFO_DEBUG + +// Older SDKs do not include the addrinfo struct declaration, so we +// include a copy of it here. +struct qt_addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + sockaddr *ai_addr; + qt_addrinfo *ai_next; +}; + +// sockaddr_in6 size changed between old and new SDK +// Only the new version is the correct one, so always +// use this structure. +struct qt_in6_addr { + uchar qt_s6_addr[16]; +}; + +struct qt_sockaddr_in6 { + short sin6_family; /* AF_INET6 */ + u_short sin6_port; /* Transport level port number */ + u_long sin6_flowinfo; /* IPv6 flow information */ + struct qt_in6_addr sin6_addr; /* IPv6 address */ + u_long sin6_scope_id; /* set of interfaces for a scope */ +}; + +//### +#define QT_SOCKLEN_T int +#ifndef NI_MAXHOST // already defined to 1025 in ws2tcpip.h? +#define NI_MAXHOST 1024 +#endif + +typedef int (__stdcall *getnameinfoProto)(const sockaddr *, QT_SOCKLEN_T, const char *, DWORD, const char *, DWORD, int); +typedef int (__stdcall *getaddrinfoProto)(const char *, const char *, const qt_addrinfo *, qt_addrinfo **); +typedef int (__stdcall *freeaddrinfoProto)(qt_addrinfo *); +static getnameinfoProto local_getnameinfo = 0; +static getaddrinfoProto local_getaddrinfo = 0; +static freeaddrinfoProto local_freeaddrinfo = 0; + +static void resolveLibrary() +{ + // Attempt to resolve getaddrinfo(); without it we'll have to fall + // back to gethostbyname(), which has no IPv6 support. +#if !defined(Q_OS_WINCE) + local_getaddrinfo = (getaddrinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "getaddrinfo"); + local_freeaddrinfo = (freeaddrinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "freeaddrinfo"); + local_getnameinfo = (getnameinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "getnameinfo"); +#else + local_getaddrinfo = (getaddrinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "getaddrinfo"); + local_freeaddrinfo = (freeaddrinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "freeaddrinfo"); + local_getnameinfo = (getnameinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "getnameinfo"); +#endif +} + +#if defined(Q_OS_WINCE) +#include <qmutex.h> +QMutex qPrivCEMutex; +#endif +/* + Performs a blocking call to gethostbyname or getaddrinfo, stores + the results in a QHostInfo structure and emits the + resultsReady() signal. +*/ +QHostInfo QHostInfoAgent::fromName(const QString &hostName) +{ +#if defined(Q_OS_WINCE) + QMutexLocker locker(&qPrivCEMutex); +#endif + QWindowsSockInit winSock; + + // Load res_init on demand. + static volatile bool triedResolve = false; + if (!triedResolve) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&local_getaddrinfo)); +#endif + if (!triedResolve) { + resolveLibrary(); + triedResolve = true; + } + } + + QHostInfo results; + results.setHostName(hostName); + +#if defined(QHOSTINFO_DEBUG) + qDebug("QHostInfoAgent::fromName(%p): looking up \"%s\" (IPv6 support is %s)", + this, hostName.toLatin1().constData(), + (local_getaddrinfo && local_freeaddrinfo) ? "enabled" : "disabled"); +#endif + + QHostAddress address; + if (address.setAddress(hostName)) { + // Reverse lookup + if (local_getnameinfo) { + sockaddr_in sa4; + qt_sockaddr_in6 sa6; + sockaddr *sa; + QT_SOCKLEN_T saSize; + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + sa = (sockaddr *)&sa4; + saSize = sizeof(sa4); + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + sa4.sin_addr.s_addr = htonl(address.toIPv4Address()); + } else { + sa = (sockaddr *)&sa6; + saSize = sizeof(sa6); + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + memcpy(sa6.sin6_addr.qt_s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.qt_s6_addr)); + } + + char hbuf[NI_MAXHOST]; + if (local_getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) != 0) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + results.setHostName(QString::fromLatin1(hbuf)); + } else { + unsigned long addr = inet_addr(hostName.toLatin1().constData()); + struct hostent *ent = gethostbyaddr((const char*)&addr, sizeof(addr), AF_INET); + if (!ent) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + results.setHostName(QString::fromLatin1(ent->h_name)); + } + } + + if (local_getaddrinfo && local_freeaddrinfo) { + // Call getaddrinfo, and place all IPv4 addresses at the start + // and the IPv6 addresses at the end of the address list in + // results. + qt_addrinfo *res; + int err = local_getaddrinfo(hostName.toLatin1().constData(), 0, 0, &res); + if (err == 0) { + QList<QHostAddress> addresses; + for (qt_addrinfo *p = res; p != 0; p = p->ai_next) { + switch (p->ai_family) { + case AF_INET: { + QHostAddress addr; + addr.setAddress(ntohl(((sockaddr_in *) p->ai_addr)->sin_addr.s_addr)); + if (!addresses.contains(addr)) + addresses.append(addr); + } + break; + case AF_INET6: { + QHostAddress addr; + addr.setAddress(((qt_sockaddr_in6 *) p->ai_addr)->sin6_addr.qt_s6_addr); + if (!addresses.contains(addr)) + addresses.append(addr); + } + break; + default: + results.setError(QHostInfo::UnknownError); + results.setErrorString(tr("Unknown address type")); + } + } + results.setAddresses(addresses); + local_freeaddrinfo(res); + } else if (WSAGetLastError() == WSAHOST_NOT_FOUND || WSAGetLastError() == WSANO_DATA) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + } else { + results.setError(QHostInfo::UnknownError); + results.setErrorString(tr("Unknown error")); + } + } else { + // Fall back to gethostbyname, which only supports IPv4. + hostent *ent = gethostbyname(hostName.toLatin1().constData()); + if (ent) { + char **p; + QList<QHostAddress> addresses; + switch (ent->h_addrtype) { + case AF_INET: + for (p = ent->h_addr_list; *p != 0; p++) { + long *ip4Addr = (long *) *p; + QHostAddress temp; + temp.setAddress(ntohl(*ip4Addr)); + addresses << temp; + } + break; + default: + results.setError(QHostInfo::UnknownError); + results.setErrorString(tr("Unknown address type")); + break; + } + results.setAddresses(addresses); + } else if (WSAGetLastError() == 11001) { + results.setErrorString(tr("Host not found")); + results.setError(QHostInfo::HostNotFound); + } else { + results.setErrorString(tr("Unknown error")); + results.setError(QHostInfo::UnknownError); + } + } + +#if defined(QHOSTINFO_DEBUG) + if (results.error() != QHostInfo::NoError) { + qDebug("QHostInfoAgent::run(%p): error (%s)", + this, results.errorString().toLatin1().constData()); + } else { + QString tmp; + QList<QHostAddress> addresses = results.addresses(); + for (int i = 0; i < addresses.count(); ++i) { + if (i != 0) tmp += ", "; + tmp += addresses.at(i).toString(); + } + qDebug("QHostInfoAgent::run(%p): found %i entries: {%s}", + this, addresses.count(), tmp.toLatin1().constData()); + } +#endif + return results; +} + +QString QHostInfo::localHostName() +{ + QWindowsSockInit winSock; + + char hostName[512]; + if (gethostname(hostName, sizeof(hostName)) == -1) + return QString(); + hostName[sizeof(hostName) - 1] = '\0'; + return QString::fromLocal8Bit(hostName); +} + +// QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp new file mode 100644 index 0000000..670745b --- /dev/null +++ b/src/network/kernel/qnetworkinterface.cpp @@ -0,0 +1,615 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkinterface.h" +#include "qnetworkinterface_p.h" + +#include "qdebug.h" +#include "qendian.h" + +#ifndef QT_NO_NETWORKINTERFACE + +QT_BEGIN_NAMESPACE + +static QList<QNetworkInterfacePrivate *> postProcess(QList<QNetworkInterfacePrivate *> list) +{ + // Some platforms report a netmask but don't report a broadcast address + // Go through all available addresses and calculate the broadcast address + // from the IP and the netmask + // + // This is an IPv4-only thing -- IPv6 has no concept of broadcasts + // The math is: + // broadcast = IP | ~netmask + + QList<QNetworkInterfacePrivate *>::Iterator it = list.begin(); + const QList<QNetworkInterfacePrivate *>::Iterator end = list.end(); + for ( ; it != end; ++it) { + QList<QNetworkAddressEntry>::Iterator addr_it = (*it)->addressEntries.begin(); + const QList<QNetworkAddressEntry>::Iterator addr_end = (*it)->addressEntries.end(); + for ( ; addr_it != addr_end; ++addr_it) { + if (addr_it->ip().protocol() != QAbstractSocket::IPv4Protocol) + continue; + + if (!addr_it->netmask().isNull() && addr_it->broadcast().isNull()) { + QHostAddress bcast = addr_it->ip(); + bcast = QHostAddress(bcast.toIPv4Address() | ~addr_it->netmask().toIPv4Address()); + addr_it->setBroadcast(bcast); + } + } + } + + return list; +} + +Q_GLOBAL_STATIC(QNetworkInterfaceManager, manager) + +QNetworkInterfaceManager::QNetworkInterfaceManager() +{ +} + +QNetworkInterfaceManager::~QNetworkInterfaceManager() +{ +} + +QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interfaceFromName(const QString &name) +{ + QList<QSharedDataPointer<QNetworkInterfacePrivate> > interfaceList = allInterfaces(); + QList<QSharedDataPointer<QNetworkInterfacePrivate> >::ConstIterator it = interfaceList.constBegin(); + for ( ; it != interfaceList.constEnd(); ++it) + if ((*it)->name == name) + return *it; + + return empty; +} + +QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interfaceFromIndex(int index) +{ + QList<QSharedDataPointer<QNetworkInterfacePrivate> > interfaceList = allInterfaces(); + QList<QSharedDataPointer<QNetworkInterfacePrivate> >::ConstIterator it = interfaceList.constBegin(); + for ( ; it != interfaceList.constEnd(); ++it) + if ((*it)->index == index) + return *it; + + return empty; +} + +QList<QSharedDataPointer<QNetworkInterfacePrivate> > QNetworkInterfaceManager::allInterfaces() +{ + QList<QNetworkInterfacePrivate *> list = postProcess(scan()); + QList<QSharedDataPointer<QNetworkInterfacePrivate> > result; + + foreach (QNetworkInterfacePrivate *ptr, list) + result << QSharedDataPointer<QNetworkInterfacePrivate>(ptr); + + return result; +} + +QString QNetworkInterfacePrivate::makeHwAddress(int len, uchar *data) +{ + QString result; + for (int i = 0; i < len; ++i) { + if (i) + result += QLatin1Char(':'); + + char buf[3]; +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400 + sprintf_s(buf, 3, "%02hX", ushort(data[i])); +#else + sprintf(buf, "%02hX", ushort(data[i])); +#endif + result += QLatin1String(buf); + } + return result; +} + +/*! + \class QNetworkAddressEntry + \brief The QNetworkAddressEntry class stores one IP address + supported by a network interface, along with its associated + netmask and broadcast address. + + \since 4.2 + \reentrant + \ingroup io + + Each network interface can contain zero or more IP addresses, which + in turn can be associated with a netmask and/or a broadcast + address (depending on support from the operating system). + + This class represents one such group. +*/ + +/*! + Constructs an empty QNetworkAddressEntry object. +*/ +QNetworkAddressEntry::QNetworkAddressEntry() + : d(new QNetworkAddressEntryPrivate) +{ +} + +/*! + Constructs a QNetworkAddressEntry object that is a copy of the + object \a other. +*/ +QNetworkAddressEntry::QNetworkAddressEntry(const QNetworkAddressEntry &other) + : d(new QNetworkAddressEntryPrivate(*other.d)) +{ +} + +/*! + Makes a copy of the QNetworkAddressEntry object \a other. +*/ +QNetworkAddressEntry &QNetworkAddressEntry::operator=(const QNetworkAddressEntry &other) +{ + *d = *other.d; + return *this; +} + +/*! + Destroys this QNetworkAddressEntry object. +*/ +QNetworkAddressEntry::~QNetworkAddressEntry() +{ + delete d; +} + +/*! + Returns true if this network address entry is the same as \a + other. +*/ +bool QNetworkAddressEntry::operator==(const QNetworkAddressEntry &other) const +{ + if (d == other.d) return true; + if (!d || !other.d) return false; + return d->address == other.d->address && + d->netmask == other.d->netmask && + d->broadcast == other.d->broadcast; +} + +/*! + \fn bool QNetworkAddressEntry::operator!=(const QNetworkAddressEntry &other) const + + Returns true if this network address entry is different from \a + other. +*/ + +/*! + This function returns one IPv4 or IPv6 address found, that was + found in a network interface. +*/ +QHostAddress QNetworkAddressEntry::ip() const +{ + return d->address; +} + +/*! + Sets the IP address the QNetworkAddressEntry object contains to \a + newIp. +*/ +void QNetworkAddressEntry::setIp(const QHostAddress &newIp) +{ + d->address = newIp; +} + +/*! + Returns the netmask associated with the IP address. The + netmask is expressed in the form of an IP address, such as + 255.255.0.0. + + For IPv6 addresses, the prefix length is converted to an address + where the number of bits set to 1 is equal to the prefix + length. For a prefix length of 64 bits (the most common value), + the netmask will be expressed as a QHostAddress holding the + address FFFF:FFFF:FFFF:FFFF:: + + \sa prefixLength() +*/ +QHostAddress QNetworkAddressEntry::netmask() const +{ + return d->netmask; +} + +/*! + Sets the netmask that this QNetworkAddressEntry object contains to + \a newNetmask. Setting the netmask also sets the prefix length to + match the new netmask. + + \sa setPrefixLength() +*/ +void QNetworkAddressEntry::setNetmask(const QHostAddress &newNetmask) +{ + if (newNetmask.protocol() != ip().protocol()) { + d->netmask = QNetmaskAddress(); + return; + } + + d->netmask.setAddress(newNetmask); +} + +/*! + \since 4.5 + Returns the prefix length of this IP address. The prefix length + matches the number of bits set to 1 in the netmask (see + netmask()). For IPv4 addresses, the value is between 0 and 32. For + IPv6 addresses, it's contained between 0 and 128 and is the + preferred form of representing addresses. + + This function returns -1 if the prefix length could not be + determined (i.e., netmask() returns a null QHostAddress()). + + \sa netmask() +*/ +int QNetworkAddressEntry::prefixLength() const +{ + return d->netmask.prefixLength(); +} + +/*! + \since 4.5 + Sets the prefix length of this IP address to \a length. The value + of \a length must be valid for this type of IP address: between 0 + and 32 for IPv4 addresses, between 0 and 128 for IPv6 + addresses. Setting to any invalid value is equivalent to setting + to -1, which means "no prefix length". + + Setting the prefix length also sets the netmask (see netmask()). + + \sa setNetmask() +*/ +void QNetworkAddressEntry::setPrefixLength(int length) +{ + d->netmask.setPrefixLength(d->address.protocol(), length); +} + +/*! + Returns the broadcast address associated with the IPv4 + address and netmask. It can usually be derived from those two by + setting to 1 the bits of the IP address where the netmask contains + a 0. (In other words, by bitwise-OR'ing the IP address with the + inverse of the netmask) + + This member is always empty for IPv6 addresses, since the concept + of broadcast has been abandoned in that system in favor of + multicast. In particular, the group of hosts corresponding to all + the nodes in the local network can be reached by the "all-nodes" + special multicast group (address FF02::1). +*/ +QHostAddress QNetworkAddressEntry::broadcast() const +{ + return d->broadcast; +} + +/*! + Sets the broadcast IP address of this QNetworkAddressEntry object + to \a newBroadcast. +*/ +void QNetworkAddressEntry::setBroadcast(const QHostAddress &newBroadcast) +{ + d->broadcast = newBroadcast; +} + +/*! + \class QNetworkInterface + \brief The QNetworkInterface class provides a listing of the host's IP + addresses and network interfaces. + + \since 4.2 + \reentrant + \ingroup io + + QNetworkInterface represents one network interface attached to the + host where the program is being run. Each network interface may + contain zero or more IP addresses, each of which is optionally + associated with a netmask and/or a broadcast address. The list of + such trios can be obtained with addressEntries(). Alternatively, + when the netmask or the broadcast addresses aren't necessary, use + the allAddresses() convenience function to obtain just the IP + addresses. + + QNetworkInterface also reports the interface's hardware address with + hardwareAddress(). + + Not all operating systems support reporting all features. Only the + IPv4 addresses are guaranteed to be listed by this class in all + platforms. In particular, IPv6 address listing is only supported + on Windows XP and more recent versions, Linux, MacOS X and the + BSDs. + + \sa QNetworkAddressEntry +*/ + +/*! + \enum QNetworkInterface::InterfaceFlag + Specifies the flags associated with this network interface. The + possible values are: + + \value IsUp the network interface is active + \value IsRunning the network interface has resources + allocated + \value CanBroadcast the network interface works in + broadcast mode + \value IsLoopBack the network interface is a loopback + interface: that is, it's a virtual + interface whose destination is the + host computer itself + \value IsPointToPoint the network interface is a + point-to-point interface: that is, + there is one, single other address + that can be directly reached by it. + \value CanMulticast the network interface supports + multicasting + + Note that one network interface cannot be both broadcast-based and + point-to-point. +*/ + +/*! + Constructs an empty network interface object. +*/ +QNetworkInterface::QNetworkInterface() + : d(0) +{ +} + +/*! + Frees the resources associated with the QNetworkInterface object. +*/ +QNetworkInterface::~QNetworkInterface() +{ +} + +/*! + Creates a copy of the the QNetworkInterface object contained in \a + other. +*/ +QNetworkInterface::QNetworkInterface(const QNetworkInterface &other) + : d(other.d) +{ +} + +/*! + Copies the contents of the QNetworkInterface object contained in \a + other into this one. +*/ +QNetworkInterface &QNetworkInterface::operator=(const QNetworkInterface &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this QNetworkInterface object contains valid + information about a network interface. +*/ +bool QNetworkInterface::isValid() const +{ + return !name().isEmpty(); +} + +/*! + \since 4.5 + Returns the interface system index, if known. This is an integer + assigned by the operating system to identify this interface and it + generally doesn't change. It matches the scope ID field in IPv6 + addresses. + + If the index isn't known, this function returns 0. +*/ +int QNetworkInterface::index() const +{ + return d ? d->index : 0; +} + +/*! + Returns the name of this network interface. On Unix systems, this + is a string containing the type of the interface and optionally a + sequence number, such as "eth0", "lo" or "pcn0". On Windows, it's + an internal ID that cannot be changed by the user. +*/ +QString QNetworkInterface::name() const +{ + return d ? d->name : QString(); +} + +/*! + \since 4.5 + + Returns the human-readable name of this network interface on + Windows, such as "Local Area Connection", if the name could be + determined. If it couldn't, this function returns the same as + name(). The human-readable name is a name that the user can modify + in the Windows Control Panel, so it may change during the + execution of the program. + + On Unix, this function currently always returns the same as + name(), since Unix systems don't store a configuration for + human-readable names. +*/ +QString QNetworkInterface::humanReadableName() const +{ + return d ? !d->friendlyName.isEmpty() ? d->friendlyName : name() : QString(); +} + +/*! + Returns the flags associated with this network interface. +*/ +QNetworkInterface::InterfaceFlags QNetworkInterface::flags() const +{ + return d ? d->flags : InterfaceFlags(0); +} + +/*! + Returns the low-level hardware address for this interface. On + Ethernet interfaces, this will be a MAC address in string + representation, separated by colons. + + Other interface types may have other types of hardware + addresses. Implementations should not depend on this function + returning a valid MAC address. +*/ +QString QNetworkInterface::hardwareAddress() const +{ + return d ? d->hardwareAddress : QString(); +} + +/*! + Returns the list of IP addresses that this interface possesses + along with their associated netmasks and broadcast addresses. + + If the netmask or broadcast address information is not necessary, + you can call the allAddresses() function to obtain just the IP + addresses. +*/ +QList<QNetworkAddressEntry> QNetworkInterface::addressEntries() const +{ + return d ? d->addressEntries : QList<QNetworkAddressEntry>(); +} + +/*! + Returns a QNetworkInterface object for the interface named \a + name. If no such interface exists, this function returns an + invalid QNetworkInterface object. + + \sa name(), isValid() +*/ +QNetworkInterface QNetworkInterface::interfaceFromName(const QString &name) +{ + QNetworkInterface result; + result.d = manager()->interfaceFromName(name); + return result; +} + +/*! + Returns a QNetworkInterface object for the interface whose internal + ID is \a index. Network interfaces have a unique identifier called + the "interface index" to distinguish it from other interfaces on + the system. Often, this value is assigned progressively and + interfaces being removed and then added again get a different + value every time. + + This index is also found in the IPv6 address' scope ID field. +*/ +QNetworkInterface QNetworkInterface::interfaceFromIndex(int index) +{ + QNetworkInterface result; + result.d = manager()->interfaceFromIndex(index); + return result; +} + +/*! + Returns a listing of all the network interfaces found on the host + machine. +*/ +QList<QNetworkInterface> QNetworkInterface::allInterfaces() +{ + QList<QSharedDataPointer<QNetworkInterfacePrivate> > privs = manager()->allInterfaces(); + QList<QNetworkInterface> result; + foreach (QSharedDataPointer<QNetworkInterfacePrivate> p, privs) { + QNetworkInterface item; + item.d = p; + result << item; + } + + return result; +} + +/*! + This convenience function returns all IP addresses found on the + host machine. It is equivalent to calling addressEntries() on all the + objects returned by allInterfaces() to obtain lists of QHostAddress + objects then calling QHostAddress::ip() on each of these. +*/ +QList<QHostAddress> QNetworkInterface::allAddresses() +{ + QList<QSharedDataPointer<QNetworkInterfacePrivate> > privs = manager()->allInterfaces(); + QList<QHostAddress> result; + foreach (const QSharedDataPointer<QNetworkInterfacePrivate> p, privs) { + foreach (const QNetworkAddressEntry &entry, p->addressEntries) + result += entry.ip(); + } + + return result; +} + +#ifndef QT_NO_DEBUG_STREAM +static inline QDebug flagsDebug(QDebug debug, QNetworkInterface::InterfaceFlags flags) +{ + if (flags & QNetworkInterface::IsUp) + debug.nospace() << "IsUp "; + if (flags & QNetworkInterface::IsRunning) + debug.nospace() << "IsRunning "; + if (flags & QNetworkInterface::CanBroadcast) + debug.nospace() << "CanBroadcast "; + if (flags & QNetworkInterface::IsLoopBack) + debug.nospace() << "IsLoopBack "; + if (flags & QNetworkInterface::IsPointToPoint) + debug.nospace() << "IsPointToPoint "; + if (flags & QNetworkInterface::CanMulticast) + debug.nospace() << "CanMulticast "; + return debug.nospace(); +} + +static inline QDebug operator<<(QDebug debug, const QNetworkAddressEntry &entry) +{ + debug.nospace() << "(address = " << entry.ip(); + if (!entry.netmask().isNull()) + debug.nospace() << ", netmask = " << entry.netmask(); + if (!entry.broadcast().isNull()) + debug.nospace() << ", broadcast = " << entry.broadcast(); + debug.nospace() << ")"; + return debug.space(); +} + +QDebug operator<<(QDebug debug, const QNetworkInterface &networkInterface) +{ + debug.nospace() << "QNetworkInterface(name = " << networkInterface.name() + << ", hardware address = " << networkInterface.hardwareAddress() + << ", flags = "; + flagsDebug(debug, networkInterface.flags()); + debug.nospace() << ", entries = " << networkInterface.addressEntries() + << ")\n"; + return debug.space(); +} +#endif + +QT_END_NAMESPACE + +#endif // QT_NO_NETWORKINTERFACE diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h new file mode 100644 index 0000000..09fbd0f --- /dev/null +++ b/src/network/kernel/qnetworkinterface.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKINTERFACE_H +#define QNETWORKINTERFACE_H + +#include <QtCore/qshareddata.h> +#include <QtNetwork/qhostaddress.h> + +#ifndef QT_NO_NETWORKINTERFACE + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +template<typename T> class QList; + +class QNetworkAddressEntryPrivate; +class Q_NETWORK_EXPORT QNetworkAddressEntry +{ +public: + QNetworkAddressEntry(); + QNetworkAddressEntry(const QNetworkAddressEntry &other); + QNetworkAddressEntry &operator=(const QNetworkAddressEntry &other); + ~QNetworkAddressEntry(); + bool operator==(const QNetworkAddressEntry &other) const; + inline bool operator!=(const QNetworkAddressEntry &other) const + { return !(*this == other); } + + QHostAddress ip() const; + void setIp(const QHostAddress &newIp); + + QHostAddress netmask() const; + void setNetmask(const QHostAddress &newNetmask); + int prefixLength() const; + void setPrefixLength(int length); + + QHostAddress broadcast() const; + void setBroadcast(const QHostAddress &newBroadcast); + +private: + QNetworkAddressEntryPrivate *d; +}; + +class QNetworkInterfacePrivate; +class Q_NETWORK_EXPORT QNetworkInterface +{ +public: + enum InterfaceFlag { + IsUp = 0x1, + IsRunning = 0x2, + CanBroadcast = 0x4, + IsLoopBack = 0x8, + IsPointToPoint = 0x10, + CanMulticast = 0x20 + }; + Q_DECLARE_FLAGS(InterfaceFlags, InterfaceFlag) + + QNetworkInterface(); + QNetworkInterface(const QNetworkInterface &other); + QNetworkInterface &operator=(const QNetworkInterface &other); + ~QNetworkInterface(); + + bool isValid() const; + + int index() const; + QString name() const; + QString humanReadableName() const; + InterfaceFlags flags() const; + QString hardwareAddress() const; + QList<QNetworkAddressEntry> addressEntries() const; + + static QNetworkInterface interfaceFromName(const QString &name); + static QNetworkInterface interfaceFromIndex(int index); + static QList<QNetworkInterface> allInterfaces(); + static QList<QHostAddress> allAddresses(); + +private: + friend class QNetworkInterfacePrivate; + QSharedDataPointer<QNetworkInterfacePrivate> d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkInterface::InterfaceFlags) + +#ifndef QT_NO_DEBUG_STREAM +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkInterface &networkInterface); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_NETWORKINTERFACE + +#endif diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h new file mode 100644 index 0000000..c07e23c --- /dev/null +++ b/src/network/kernel/qnetworkinterface_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKINTERFACEPRIVATE_H +#define QNETWORKINTERFACEPRIVATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qatomic.h> +#include <QtCore/qlist.h> +#include <QtCore/qreadwritelock.h> +#include <QtCore/qstring.h> +#include <QtNetwork/qhostaddress.h> +#include <QtNetwork/qabstractsocket.h> +#include <private/qhostaddress_p.h> + +#ifndef QT_NO_NETWORKINTERFACE + +QT_BEGIN_NAMESPACE + +class QNetworkAddressEntryPrivate +{ +public: + QHostAddress address; + QNetmaskAddress netmask; + QHostAddress broadcast; +}; + +class QNetworkInterfacePrivate: public QSharedData +{ +public: + QNetworkInterfacePrivate() : index(0), flags(0) + { } + ~QNetworkInterfacePrivate() + { } + + int index; // interface index, if know + QNetworkInterface::InterfaceFlags flags; + + QString name; + QString friendlyName; + QString hardwareAddress; + + QList<QNetworkAddressEntry> addressEntries; + + static QString makeHwAddress(int len, uchar *data); + +private: + // disallow copying -- avoid detaching + QNetworkInterfacePrivate &operator=(const QNetworkInterfacePrivate &other); + QNetworkInterfacePrivate(const QNetworkInterfacePrivate &other); +}; + +class QNetworkInterfaceManager +{ +public: + QNetworkInterfaceManager(); + ~QNetworkInterfaceManager(); + + QSharedDataPointer<QNetworkInterfacePrivate> interfaceFromName(const QString &name); + QSharedDataPointer<QNetworkInterfacePrivate> interfaceFromIndex(int index); + QList<QSharedDataPointer<QNetworkInterfacePrivate> > allInterfaces(); + + // convenience: + QSharedDataPointer<QNetworkInterfacePrivate> empty; + +private: + QList<QNetworkInterfacePrivate *> scan(); +}; + + +QT_END_NAMESPACE + +#endif // QT_NO_NETWORKINTERFACE + +#endif diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp new file mode 100644 index 0000000..34a44ac --- /dev/null +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qset.h" +#include "qnetworkinterface.h" +#include "qnetworkinterface_p.h" +#include "qalgorithms.h" + +#ifndef QT_NO_NETWORKINTERFACE + +#define IP_MULTICAST // make AIX happy and define IFF_MULTICAST + +#include <sys/types.h> +#include <sys/socket.h> + +#ifdef Q_OS_SOLARIS +# include <sys/sockio.h> +#endif +#include <net/if.h> + +#ifndef QT_NO_GETIFADDRS +# include <ifaddrs.h> +#endif + +#ifdef QT_LINUXBASE +# include <arpa/inet.h> +# ifndef SIOCGIFBRDADDR +# define SIOCGIFBRDADDR 0x8919 +# endif +#endif // QT_LINUXBASE + +#include <qplatformdefs.h> + +QT_BEGIN_NAMESPACE + +static QHostAddress addressFromSockaddr(sockaddr *sa) +{ + QHostAddress address; + if (!sa) + return address; + + if (sa->sa_family == AF_INET) + address.setAddress(htonl(((sockaddr_in *)sa)->sin_addr.s_addr)); +#ifndef QT_NO_IPV6 + else if (sa->sa_family == AF_INET6) + address.setAddress(((sockaddr_in6 *)sa)->sin6_addr.s6_addr); +#endif + return address; + +} + +static QNetworkInterface::InterfaceFlags convertFlags(uint rawFlags) +{ + QNetworkInterface::InterfaceFlags flags = 0; + flags |= (rawFlags & IFF_UP) ? QNetworkInterface::IsUp : QNetworkInterface::InterfaceFlag(0); + flags |= (rawFlags & IFF_RUNNING) ? QNetworkInterface::IsRunning : QNetworkInterface::InterfaceFlag(0); + flags |= (rawFlags & IFF_BROADCAST) ? QNetworkInterface::CanBroadcast : QNetworkInterface::InterfaceFlag(0); + flags |= (rawFlags & IFF_LOOPBACK) ? QNetworkInterface::IsLoopBack : QNetworkInterface::InterfaceFlag(0); +#ifdef IFF_POINTOPOINT //cygwin doesn't define IFF_POINTOPOINT + flags |= (rawFlags & IFF_POINTOPOINT) ? QNetworkInterface::IsPointToPoint : QNetworkInterface::InterfaceFlag(0); +#endif + +#ifdef IFF_MULTICAST + flags |= (rawFlags & IFF_MULTICAST) ? QNetworkInterface::CanMulticast : QNetworkInterface::InterfaceFlag(0); +#endif + return flags; +} + +#ifdef QT_NO_GETIFADDRS +// getifaddrs not available + +static const int STORAGEBUFFER_GROWTH = 256; + +static QSet<QByteArray> interfaceNames(int socket) +{ + QSet<QByteArray> result; +#ifdef QT_NO_IPV6IFNAME + QByteArray storageBuffer; + struct ifconf interfaceList; + + forever { + // grow the storage buffer + storageBuffer.resize(storageBuffer.size() + STORAGEBUFFER_GROWTH); + interfaceList.ifc_buf = storageBuffer.data(); + interfaceList.ifc_len = storageBuffer.size(); + + // get the interface list + if (::ioctl(socket, SIOCGIFCONF, &interfaceList) >= 0) { + if (int(interfaceList.ifc_len + sizeof(ifreq) + 64) < storageBuffer.size()) { + // if the buffer was big enough, break + storageBuffer.resize(interfaceList.ifc_len); + break; + } + } else { + // internal error + return result; + } + if (storageBuffer.size() > 100000) { + // out of space + return result; + } + } + + int interfaceCount = interfaceList.ifc_len / sizeof(ifreq); + for (int i = 0; i < interfaceCount; ++i) { + QByteArray name = QByteArray(interfaceList.ifc_req[i].ifr_name); + if (!name.isEmpty()) + result << name; + } + + return result; +#else + Q_UNUSED(socket); + + // use if_nameindex + struct if_nameindex *interfaceList = ::if_nameindex(); + for (struct if_nameindex *ptr = interfaceList; ptr && ptr->if_name; ++ptr) + result << ptr->if_name; + + if_freenameindex(interfaceList); + return result; +#endif +} + +static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfacePrivate *> &interfaces, + struct ifreq &req) +{ + QNetworkInterfacePrivate *iface = 0; + int ifindex = 0; + +#ifndef QT_NO_IPV6IFNAME + // Get the interface index + ifindex = if_nametoindex(req.ifr_name); + + // find the interface data + QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin(); + for ( ; if_it != interfaces.end(); ++if_it) + if ((*if_it)->index == ifindex) { + // existing interface + iface = *if_it; + break; + } +#else + // Search by name + QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin(); + for ( ; if_it != interfaces.end(); ++if_it) + if ((*if_it)->name == QLatin1String(req.ifr_name)) { + // existing interface + iface = *if_it; + break; + } +#endif + + if (!iface) { + // new interface, create data: + iface = new QNetworkInterfacePrivate; + iface->index = ifindex; + interfaces << iface; + +#ifdef SIOCGIFNAME + // Get the canonical name + QByteArray oldName = req.ifr_name; + if (::ioctl(socket, SIOCGIFNAME, &req) >= 0) { + iface->name = QString::fromLatin1(req.ifr_name); + + // reset the name: + memcpy(req.ifr_name, oldName, qMin<int>(oldName.length() + 1, sizeof(req.ifr_name) - 1)); + } else +#endif + { + // use this name anyways + iface->name = QString::fromLatin1(req.ifr_name); + } + + // Get interface flags + if (::ioctl(socket, SIOCGIFFLAGS, &req) >= 0) { + iface->flags = convertFlags(req.ifr_flags); + } + +#ifdef SIOCGIFHWADDR + // Get the HW address + if (::ioctl(socket, SIOCGIFHWADDR, &req) >= 0) { + uchar *addr = (uchar *)&req.ifr_addr; + iface->hardwareAddress = iface->makeHwAddress(6, addr); + } +#endif + } + + return iface; +} + +static QList<QNetworkInterfacePrivate *> interfaceListing() +{ + QList<QNetworkInterfacePrivate *> interfaces; + + int socket; + if ((socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1) + return interfaces; // error + + QSet<QByteArray> names = interfaceNames(socket); + QSet<QByteArray>::ConstIterator it = names.constBegin(); + for ( ; it != names.constEnd(); ++it) { + ifreq req; + memset(&req, 0, sizeof(ifreq)); + memcpy(req.ifr_name, *it, qMin<int>(it->length() + 1, sizeof(req.ifr_name) - 1)); + + QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req); + + // Get the interface broadcast address + QNetworkAddressEntry entry; + if (iface->flags & QNetworkInterface::CanBroadcast) { + if (::ioctl(socket, SIOCGIFBRDADDR, &req) >= 0) { + sockaddr *sa = &req.ifr_addr; + if (sa->sa_family == AF_INET) + entry.setBroadcast(addressFromSockaddr(sa)); + } + } + + // Get the interface netmask + if (::ioctl(socket, SIOCGIFNETMASK, &req) >= 0) { + sockaddr *sa = &req.ifr_addr; + entry.setNetmask(addressFromSockaddr(sa)); + } + + // Get the address of the interface + if (::ioctl(socket, SIOCGIFADDR, &req) >= 0) { + sockaddr *sa = &req.ifr_addr; + entry.setIp(addressFromSockaddr(sa)); + } + + iface->addressEntries << entry; + } + + ::close(socket); + return interfaces; +} + +#else +// use getifaddrs + +// platform-specific defs: +# ifdef Q_OS_LINUX +QT_BEGIN_INCLUDE_NAMESPACE +# include <features.h> +QT_END_INCLUDE_NAMESPACE +# endif + +# if defined(Q_OS_LINUX) && __GLIBC__ - 0 >= 2 && __GLIBC_MINOR__ - 0 >= 1 +# include <netpacket/packet.h> + +static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) +{ + QList<QNetworkInterfacePrivate *> interfaces; + + for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) { + if ( !ptr->ifa_addr ) + continue; + + // Get the interface index + int ifindex = if_nametoindex(ptr->ifa_name); + + // on Linux we use AF_PACKET and sockaddr_ll to obtain hHwAddress + QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin(); + for ( ; if_it != interfaces.end(); ++if_it) + if ((*if_it)->index == ifindex) { + // this one has been added already + if ( ptr->ifa_addr->sa_family == AF_PACKET + && (*if_it)->hardwareAddress.isEmpty()) { + sockaddr_ll *sll = (sockaddr_ll *)ptr->ifa_addr; + (*if_it)->hardwareAddress = (*if_it)->makeHwAddress(sll->sll_halen, (uchar*)sll->sll_addr); + } + break; + } + if ( if_it != interfaces.end() ) + continue; + + QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; + interfaces << iface; + iface->index = ifindex; + iface->name = QString::fromLatin1(ptr->ifa_name); + iface->flags = convertFlags(ptr->ifa_flags); + + if ( ptr->ifa_addr->sa_family == AF_PACKET ) { + sockaddr_ll *sll = (sockaddr_ll *)ptr->ifa_addr; + iface->hardwareAddress = iface->makeHwAddress(sll->sll_halen, (uchar*)sll->sll_addr); + } + } + + return interfaces; +} + +# elif defined(Q_OS_BSD4) +QT_BEGIN_INCLUDE_NAMESPACE +# include <net/if_dl.h> +QT_END_INCLUDE_NAMESPACE + +static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) +{ + QList<QNetworkInterfacePrivate *> interfaces; + + // on NetBSD we use AF_LINK and sockaddr_dl + // scan the list for that family + for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) + if (ptr->ifa_addr && ptr->ifa_addr->sa_family == AF_LINK) { + QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; + interfaces << iface; + + sockaddr_dl *sdl = (sockaddr_dl *)ptr->ifa_addr; + iface->index = sdl->sdl_index; + iface->name = QString::fromLatin1(ptr->ifa_name); + iface->flags = convertFlags(ptr->ifa_flags); + iface->hardwareAddress = iface->makeHwAddress(sdl->sdl_alen, (uchar*)LLADDR(sdl)); + } + + return interfaces; +} + +# else // Generic version + +static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList) +{ + QList<QNetworkInterfacePrivate *> interfaces; + + // make sure there's one entry for each interface + for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) { + // Get the interface index + int ifindex = if_nametoindex(ptr->ifa_name); + + QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin(); + for ( ; if_it != interfaces.end(); ++if_it) + if ((*if_it)->index == ifindex) + // this one has been added already + break; + + if (if_it == interfaces.end()) { + // none found, create + QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; + interfaces << iface; + + iface->index = ifindex; + iface->name = QString::fromLatin1(ptr->ifa_name); + iface->flags = convertFlags(ptr->ifa_flags); + } + } + + return interfaces; +} + +# endif + + +static QList<QNetworkInterfacePrivate *> interfaceListing() +{ + QList<QNetworkInterfacePrivate *> interfaces; + + int socket; + if ((socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1) + return interfaces; // error + + ifaddrs *interfaceListing; + if (getifaddrs(&interfaceListing) == -1) { + // error + ::close(socket); + return interfaces; + } + + interfaces = createInterfaces(interfaceListing); + for (ifaddrs *ptr = interfaceListing; ptr; ptr = ptr->ifa_next) { + // Get the interface index + int ifindex = if_nametoindex(ptr->ifa_name); + QNetworkInterfacePrivate *iface = 0; + QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin(); + for ( ; if_it != interfaces.end(); ++if_it) + if ((*if_it)->index == ifindex) { + // found this interface already + iface = *if_it; + break; + } + if (!iface) { + // skip all non-IP interfaces + continue; + } + + QNetworkAddressEntry entry; + entry.setIp(addressFromSockaddr(ptr->ifa_addr)); + if (entry.ip().isNull()) + // could not parse the address + continue; + + entry.setNetmask(addressFromSockaddr(ptr->ifa_netmask)); + if (iface->flags & QNetworkInterface::CanBroadcast) + entry.setBroadcast(addressFromSockaddr(ptr->ifa_broadaddr)); + + iface->addressEntries << entry; + } + + freeifaddrs(interfaceListing); + ::close(socket); + return interfaces; +} +#endif + +QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan() +{ + return interfaceListing(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_NETWORKINTERFACE diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp new file mode 100644 index 0000000..9540c18 --- /dev/null +++ b/src/network/kernel/qnetworkinterface_win.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkinterface.h" +#include "qnetworkinterface_p.h" + +#ifndef QT_NO_NETWORKINTERFACE + +#include "qnetworkinterface_win_p.h" +#include <qhostinfo.h> +#include <qhash.h> +#include <qurl.h> + +QT_BEGIN_NAMESPACE + +typedef DWORD (WINAPI *PtrGetAdaptersInfo)(PIP_ADAPTER_INFO, PULONG); +static PtrGetAdaptersInfo ptrGetAdaptersInfo = 0; +typedef ULONG (WINAPI *PtrGetAdaptersAddresses)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG); +static PtrGetAdaptersAddresses ptrGetAdaptersAddresses = 0; +typedef DWORD (WINAPI *PtrGetNetworkParams)(PFIXED_INFO, PULONG); +static PtrGetNetworkParams ptrGetNetworkParams = 0; + +static void resolveLibs() +{ + // try to find the functions we need from Iphlpapi.dll + static bool done = false; + + if (!done) { + done = true; + + HINSTANCE iphlpapiHnd; + QT_WA({ + iphlpapiHnd = LoadLibraryW(L"iphlpapi"); + }, { + iphlpapiHnd = LoadLibraryA("iphlpapi"); + }); + if (iphlpapiHnd == NULL) + return; // failed to load, probably Windows 95 + +#if defined(Q_OS_WINCE) + ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddressW(iphlpapiHnd, L"GetAdaptersInfo"); + ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddressW(iphlpapiHnd, L"GetAdaptersAddresses"); + ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddressW(iphlpapiHnd, L"GetNetworkParams"); +#else + ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddress(iphlpapiHnd, "GetAdaptersInfo"); + ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddress(iphlpapiHnd, "GetAdaptersAddresses"); + ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddress(iphlpapiHnd, "GetNetworkParams"); +#endif + } +} + +static QHostAddress addressFromSockaddr(sockaddr *sa) +{ + QHostAddress address; + if (!sa) + return address; + + if (sa->sa_family == AF_INET) + address.setAddress(htonl(((sockaddr_in *)sa)->sin_addr.s_addr)); + else if (sa->sa_family == AF_INET6) + address.setAddress(((qt_sockaddr_in6 *)sa)->sin6_addr.qt_s6_addr); + else + qWarning("Got unknown socket family %d", sa->sa_family); + return address; + +} + +static QHash<QHostAddress, QHostAddress> ipv4Netmasks() +{ + //Retrieve all the IPV4 addresses & netmasks + IP_ADAPTER_INFO staticBuf[2]; // 2 is arbitrary + PIP_ADAPTER_INFO pAdapter = staticBuf; + ULONG bufSize = sizeof staticBuf; + QHash<QHostAddress, QHostAddress> ipv4netmasks; + + DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize); + if (retval == ERROR_BUFFER_OVERFLOW) { + // need more memory + pAdapter = (IP_ADAPTER_INFO *)qMalloc(bufSize); + // try again + if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) { + qFree(pAdapter); + return ipv4netmasks; + } + } else if (retval != ERROR_SUCCESS) { + // error + return ipv4netmasks; + } + + // iterate over the list and add the entries to our listing + for (PIP_ADAPTER_INFO ptr = pAdapter; ptr; ptr = ptr->Next) { + for (PIP_ADDR_STRING addr = &ptr->IpAddressList; addr; addr = addr->Next) { + QHostAddress address(QLatin1String(addr->IpAddress.String)); + QHostAddress mask(QLatin1String(addr->IpMask.String)); + ipv4netmasks[address] = mask; + } + } + if (pAdapter != staticBuf) + qFree(pAdapter); + + return ipv4netmasks; + +} + +static QList<QNetworkInterfacePrivate *> interfaceListingWinXP() +{ + QList<QNetworkInterfacePrivate *> interfaces; + IP_ADAPTER_ADDRESSES staticBuf[2]; // 2 is arbitrary + PIP_ADAPTER_ADDRESSES pAdapter = staticBuf; + ULONG bufSize = sizeof staticBuf; + + const QHash<QHostAddress, QHostAddress> &ipv4netmasks = ipv4Netmasks(); + ULONG flags = GAA_FLAG_INCLUDE_ALL_INTERFACES | + GAA_FLAG_INCLUDE_PREFIX | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_MULTICAST; + ULONG retval = ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize); + if (retval == ERROR_BUFFER_OVERFLOW) { + // need more memory + pAdapter = (IP_ADAPTER_ADDRESSES *)qMalloc(bufSize); + + // try again + if (ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize) != ERROR_SUCCESS) { + qFree(pAdapter); + return interfaces; + } + } else if (retval != ERROR_SUCCESS) { + // error + return interfaces; + } + + // iterate over the list and add the entries to our listing + for (PIP_ADAPTER_ADDRESSES ptr = pAdapter; ptr; ptr = ptr->Next) { + QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; + interfaces << iface; + + iface->index = 0; + if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Ipv6IfIndex)) + iface->index = ptr->Ipv6IfIndex; + else if (ptr->IfIndex != 0) + iface->index = ptr->IfIndex; + + iface->flags = QNetworkInterface::CanBroadcast; + if (ptr->OperStatus == IfOperStatusUp) + iface->flags |= QNetworkInterface::IsUp | QNetworkInterface::IsRunning; + if ((ptr->Flags & IP_ADAPTER_NO_MULTICAST) == 0) + iface->flags |= QNetworkInterface::CanMulticast; + + iface->name = QString::fromLocal8Bit(ptr->AdapterName); + iface->friendlyName = QString::fromWCharArray(ptr->FriendlyName); + if (ptr->PhysicalAddressLength) + iface->hardwareAddress = iface->makeHwAddress(ptr->PhysicalAddressLength, + ptr->PhysicalAddress); + else + // loopback if it has no address + iface->flags |= QNetworkInterface::IsLoopBack; + + // The GetAdaptersAddresses call has an interesting semantic: + // It can return a number N of addresses and a number M of prefixes. + // But if you have IPv6 addresses, generally N > M. + // I cannot find a way to relate the Address to the Prefix, aside from stopping + // the iteration at the last Prefix entry and assume that it applies to all addresses + // from that point on. + PIP_ADAPTER_PREFIX pprefix = 0; + if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, FirstPrefix)) + pprefix = ptr->FirstPrefix; + for (PIP_ADAPTER_UNICAST_ADDRESS addr = ptr->FirstUnicastAddress; addr; addr = addr->Next) { + QNetworkAddressEntry entry; + entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr)); + if (pprefix) { + if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { + entry.setNetmask(ipv4netmasks[entry.ip()]); + + // broadcast address is set on postProcess() + } else { //IPV6 + entry.setPrefixLength(pprefix->PrefixLength); + } + pprefix = pprefix->Next ? pprefix->Next : pprefix; + } + iface->addressEntries << entry; + } + } + + if (pAdapter != staticBuf) + qFree(pAdapter); + + return interfaces; +} + +static QList<QNetworkInterfacePrivate *> interfaceListingWin2k() +{ + QList<QNetworkInterfacePrivate *> interfaces; + IP_ADAPTER_INFO staticBuf[2]; // 2 is arbitrary + PIP_ADAPTER_INFO pAdapter = staticBuf; + ULONG bufSize = sizeof staticBuf; + + DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize); + if (retval == ERROR_BUFFER_OVERFLOW) { + // need more memory + pAdapter = (IP_ADAPTER_INFO *)qMalloc(bufSize); + + // try again + if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) { + qFree(pAdapter); + return interfaces; + } + } else if (retval != ERROR_SUCCESS) { + // error + return interfaces; + } + + // iterate over the list and add the entries to our listing + for (PIP_ADAPTER_INFO ptr = pAdapter; ptr; ptr = ptr->Next) { + QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate; + interfaces << iface; + + iface->index = ptr->Index; + iface->flags = QNetworkInterface::IsUp | QNetworkInterface::IsRunning; + if (ptr->Type == MIB_IF_TYPE_PPP) + iface->flags |= QNetworkInterface::IsPointToPoint; + else + iface->flags |= QNetworkInterface::CanBroadcast; + iface->name = QString::fromLocal8Bit(ptr->AdapterName); + iface->hardwareAddress = QNetworkInterfacePrivate::makeHwAddress(ptr->AddressLength, + ptr->Address); + + for (PIP_ADDR_STRING addr = &ptr->IpAddressList; addr; addr = addr->Next) { + QNetworkAddressEntry entry; + entry.setIp(QHostAddress(QLatin1String(addr->IpAddress.String))); + entry.setNetmask(QHostAddress(QLatin1String(addr->IpMask.String))); + // broadcast address is set on postProcess() + + iface->addressEntries << entry; + } + } + + if (pAdapter != staticBuf) + qFree(pAdapter); + + return interfaces; +} + +static QList<QNetworkInterfacePrivate *> interfaceListing() +{ + resolveLibs(); + if (ptrGetAdaptersAddresses != NULL) + return interfaceListingWinXP(); + else if (ptrGetAdaptersInfo != NULL) + return interfaceListingWin2k(); + + // failed + return QList<QNetworkInterfacePrivate *>(); +} + +QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan() +{ + return interfaceListing(); +} + +QString QHostInfo::localDomainName() +{ + resolveLibs(); + if (ptrGetNetworkParams == NULL) + return QString(); // couldn't resolve + + FIXED_INFO info, *pinfo; + ULONG bufSize = sizeof info; + pinfo = &info; + if (ptrGetNetworkParams(pinfo, &bufSize) == ERROR_BUFFER_OVERFLOW) { + pinfo = (FIXED_INFO *)qMalloc(bufSize); + + // try again + if (ptrGetNetworkParams(pinfo, &bufSize) != ERROR_SUCCESS) { + qFree(pinfo); + return QString(); // error + } + } + + QString domainName = QUrl::fromAce(pinfo->DomainName); + + if (pinfo != &info) + qFree(pinfo); + + return domainName; +} + +QT_END_NAMESPACE + +#endif // QT_NO_NETWORKINTERFACE diff --git a/src/network/kernel/qnetworkinterface_win_p.h b/src/network/kernel/qnetworkinterface_win_p.h new file mode 100644 index 0000000..07425d0 --- /dev/null +++ b/src/network/kernel/qnetworkinterface_win_p.h @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKINTERFACE_WIN_P_H +#define QNETWORKINTERFACE_WIN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <winsock2.h> +#include <windows.h> +#include <time.h> + +QT_BEGIN_NAMESPACE + +#ifndef GAA_FLAG_INCLUDE_ALL_INTERFACES +# define GAA_FLAG_INCLUDE_ALL_INTERFACES 0x0100 +#endif +#ifndef MAX_ADAPTER_ADDRESS_LENGTH +// definitions from iptypes.h +# define MAX_ADAPTER_DESCRIPTION_LENGTH 128 // arb. +# define MAX_ADAPTER_NAME_LENGTH 256 // arb. +# define MAX_ADAPTER_ADDRESS_LENGTH 8 // arb. +# define DEFAULT_MINIMUM_ENTITIES 32 // arb. +# define MAX_HOSTNAME_LEN 128 // arb. +# define MAX_DOMAIN_NAME_LEN 128 // arb. +# define MAX_SCOPE_ID_LEN 256 // arb. + +# define GAA_FLAG_SKIP_UNICAST 0x0001 +# define GAA_FLAG_SKIP_ANYCAST 0x0002 +# define GAA_FLAG_SKIP_MULTICAST 0x0004 +# define GAA_FLAG_SKIP_DNS_SERVER 0x0008 +# define GAA_FLAG_INCLUDE_PREFIX 0x0010 +# define GAA_FLAG_SKIP_FRIENDLY_NAME 0x0020 + +# define IP_ADAPTER_DDNS_ENABLED 0x01 +# define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX 0x02 +# define IP_ADAPTER_DHCP_ENABLED 0x04 +# define IP_ADAPTER_RECEIVE_ONLY 0x08 +# define IP_ADAPTER_NO_MULTICAST 0x10 +# define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20 + +# define MIB_IF_TYPE_OTHER 1 +# define MIB_IF_TYPE_ETHERNET 6 +# define MIB_IF_TYPE_TOKENRING 9 +# define MIB_IF_TYPE_FDDI 15 +# define MIB_IF_TYPE_PPP 23 +# define MIB_IF_TYPE_LOOPBACK 24 +# define MIB_IF_TYPE_SLIP 28 + +#endif +// copied from qnativesocketengine_win.cpp +struct qt_in6_addr { + u_char qt_s6_addr[16]; +}; +typedef struct { + short sin6_family; /* AF_INET6 */ + u_short sin6_port; /* Transport level port number */ + u_long sin6_flowinfo; /* IPv6 flow information */ + struct qt_in6_addr sin6_addr; /* IPv6 address */ + u_long sin6_scope_id; /* set of interfaces for a scope */ +} qt_sockaddr_in6; + +// copied from MSDN online help +typedef enum { + IpPrefixOriginOther = 0, + IpPrefixOriginManual, + IpPrefixOriginWellKnown, + IpPrefixOriginDhcp, + IpPrefixOriginRouterAdvertisement +} IP_PREFIX_ORIGIN; + +typedef enum { + IpSuffixOriginOther = 0, + IpSuffixOriginManual, + IpSuffixOriginWellKnown, + IpSuffixOriginDhcp, + IpSuffixOriginLinkLayerAddress, + IpSuffixOriginRandom +} IP_SUFFIX_ORIGIN; + +typedef enum { + IpDadStateInvalid = 0, + IpDadStateTentative, + IpDadStateDuplicate, + IpDadStateDeprecated, + IpDadStatePreferred, +} IP_DAD_STATE; + +typedef enum { + IfOperStatusUp = 1, + IfOperStatusDown, + IfOperStatusTesting, + IfOperStatusUnknown, + IfOperStatusDormant, + IfOperStatusNotPresent, + IfOperStatusLowerLayerDown +} IF_OPER_STATUS; + +typedef struct _IP_ADAPTER_UNICAST_ADDRESS { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD Flags; + }; + }; + struct _IP_ADAPTER_UNICAST_ADDRESS* Next; + SOCKET_ADDRESS Address; + IP_PREFIX_ORIGIN PrefixOrigin; + IP_SUFFIX_ORIGIN SuffixOrigin; + IP_DAD_STATE DadState; + ULONG ValidLifetime; + ULONG PreferredLifetime; + ULONG LeaseLifetime; +} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS; + +typedef struct _IP_ADAPTER_ANYCAST_ADDRESS + IP_ADAPTER_ANYCAST_ADDRESS, *PIP_ADAPTER_ANYCAST_ADDRESS; + +typedef struct _IP_ADAPTER_MULTICAST_ADDRESS + IP_ADAPTER_MULTICAST_ADDRESS, + *PIP_ADAPTER_MULTICAST_ADDRESS; + +typedef struct _IP_ADAPTER_DNS_SERVER_ADDRESS + IP_ADAPTER_DNS_SERVER_ADDRESS, + *PIP_ADAPTER_DNS_SERVER_ADDRESS; + +typedef struct _IP_ADAPTER_PREFIX { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD Flags; + }; + }; + struct _IP_ADAPTER_PREFIX* Next; + SOCKET_ADDRESS Address; + ULONG PrefixLength; +} IP_ADAPTER_PREFIX, + *PIP_ADAPTER_PREFIX; + +typedef struct _IP_ADAPTER_ADDRESSES { + union { + ULONGLONG Alignment; + struct { + ULONG Length; + DWORD IfIndex; + }; + }; + struct _IP_ADAPTER_ADDRESSES* Next; + PCHAR AdapterName; + PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress; + PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress; + PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress; + PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress; + PWCHAR DnsSuffix; + PWCHAR Description; + PWCHAR FriendlyName; + BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; + DWORD PhysicalAddressLength; + DWORD Flags; + DWORD Mtu; + DWORD IfType; + IF_OPER_STATUS OperStatus; + DWORD Ipv6IfIndex; + DWORD ZoneIndices[16]; + PIP_ADAPTER_PREFIX FirstPrefix; +} IP_ADAPTER_ADDRESSES, + *PIP_ADAPTER_ADDRESSES; + +typedef struct { + char String[4 * 4]; +} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; + +typedef struct _IP_ADDR_STRING { + struct _IP_ADDR_STRING* Next; + IP_ADDRESS_STRING IpAddress; + IP_MASK_STRING IpMask; + DWORD Context; +} IP_ADDR_STRING, + *PIP_ADDR_STRING; + +typedef struct _IP_ADAPTER_INFO { + struct _IP_ADAPTER_INFO* Next; + DWORD ComboIndex; + char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4]; + char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]; + UINT AddressLength; + BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]; + DWORD Index; + UINT Type; + UINT DhcpEnabled; + PIP_ADDR_STRING CurrentIpAddress; + IP_ADDR_STRING IpAddressList; + IP_ADDR_STRING GatewayList; + IP_ADDR_STRING DhcpServer; + BOOL HaveWins; + IP_ADDR_STRING PrimaryWinsServer; + IP_ADDR_STRING SecondaryWinsServer; + time_t LeaseObtained; + time_t LeaseExpires; +} IP_ADAPTER_INFO, + *PIP_ADAPTER_INFO; + +typedef struct { + char HostName[MAX_HOSTNAME_LEN + 4]; + char DomainName[MAX_DOMAIN_NAME_LEN + 4]; + PIP_ADDR_STRING CurrentDnsServer; + IP_ADDR_STRING DnsServerList; + UINT NodeType; + char ScopeId[MAX_SCOPE_ID_LEN + 4]; + UINT EnableRouting; + UINT EnableProxy; + UINT EnableDns; +} FIXED_INFO, *PFIXED_INFO; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp new file mode 100644 index 0000000..f4ece97 --- /dev/null +++ b/src/network/kernel/qnetworkproxy.cpp @@ -0,0 +1,1255 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QNetworkProxy + + \since 4.1 + + \brief The QNetworkProxy class provides a network layer proxy. + + \reentrant + \ingroup io + \inmodule QtNetwork + + QNetworkProxy provides the method for configuring network layer + proxy support to the Qt network classes. The currently supported + classes are QAbstractSocket, QTcpSocket, QUdpSocket, QTcpServer, + QHttp and QFtp. The proxy support is designed to be as transparent + as possible. This means that existing network-enabled applications + that you have written should automatically support network proxy + using the following code. + + \snippet doc/src/snippets/code/src_network_kernel_qnetworkproxy.cpp 0 + + An alternative to setting an application wide proxy is to specify + the proxy for individual sockets using QAbstractSocket::setProxy() + and QTcpServer::setProxy(). In this way, it is possible to disable + the use of a proxy for specific sockets using the following code: + + \snippet doc/src/snippets/code/src_network_kernel_qnetworkproxy.cpp 1 + + Network proxy is not used if the address used in \l + {QAbstractSocket::connectToHost()}{connectToHost()}, \l + {QUdpSocket::bind()}{bind()} or \l + {QTcpServer::listen()}{listen()} is equivalent to + QHostAddress::LocalHost or QHostAddress::LocalHostIPv6. + + Each type of proxy support has certain restrictions associated with it. + You should read the \l{ProxyType} documentation carefully before + selecting a proxy type to use. + + \note Changes made to currently connected sockets do not take effect. + If you need to change a connected socket, you should reconnect it. + + \section1 SOCKS5 + + The SOCKS5 support in Qt 4 is based on \l{RFC 1928} and \l{RFC 1929}. + The supported authentication methods are no authentication and + username/password authentication. Both IPv4 and IPv6 are + supported, but domain name resolution via the SOCKS server is not + supported; i.e. all domain names are resolved locally. There are + several things to remember when using SOCKS5 with QUdpSocket and + QTcpServer: + + With QUdpSocket, a call to \l {QUdpSocket::bind()}{bind()} may fail + with a timeout error. If a port number other than 0 is passed to + \l {QUdpSocket::bind()}{bind()}, it is not guaranteed that it is the + specified port that will be used. + Use \l{QUdpSocket::localPort()}{localPort()} and + \l{QUdpSocket::localAddress()}{localAddress()} to get the actual + address and port number in use. Because proxied UDP goes through + two UDP connections, it is more likely that packets will be dropped. + + With QTcpServer a call to \l{QTcpServer::listen()}{listen()} may + fail with a timeout error. If a port number other than 0 is passed + to \l{QTcpServer::listen()}{listen()}, then it is not guaranteed + that it is the specified port that will be used. + Use \l{QTcpServer::serverPort()}{serverPort()} and + \l{QTcpServer::serverAddress()}{serverAddress()} to get the actual + address and port used to listen for connections. SOCKS5 only supports + one accepted connection per call to \l{QTcpServer::listen()}{listen()}, + and each call is likely to result in a different + \l{QTcpServer::serverPort()}{serverPort()} being used. + + \sa QAbstractSocket, QTcpServer +*/ + +/*! + \enum QNetworkProxy::ProxyType + + This enum describes the types of network proxying provided in Qt. + + There are two types of proxies that Qt understands: + transparent proxies and caching proxies. The first group consists + of proxies that can handle any arbitrary data transfer, while the + second can only handle specific requests. The caching proxies only + make sense for the specific classes where they can be used. + + \value NoProxy No proxying is used + \value DefaultProxy Proxy is determined based on the application proxy set using setApplicationProxy() + \value Socks5Proxy \l Socks5 proxying is used + \value HttpProxy HTTP transparent proxying is used + \value HttpCachingProxy Proxying for HTTP requests only + \value FtpCachingProxy Proxying for FTP requests only + + The table below lists different proxy types and their + capabilities. Since each proxy type has different capabilities, it + is important to understand them before choosing a proxy type. + + \table + \header + \o Proxy type + \o Description + \o Default capabilities + + \row + \o SOCKS 5 + \o Generic proxy for any kind of connection. Supports TCP, + UDP, binding to a port (incoming connections) and + authentication. + \o TunnelingCapability, ListeningCapability, + UdpTunnelingCapability, HostNameLookupCapability + + \row + \o HTTP + \o Implemented using the "CONNECT" command, supports only + outgoing TCP connections; supports authentication. + \o TunnelingCapability, CachingCapability, HostNameLookupCapability + + \row + \o Caching-only HTTP + \o Implemented using normal HTTP commands, it is useful only + in the context of HTTP requests (see QHttp, + QNetworkAccessManager) + \o CachingCapability, HostNameLookupCapability + + \row + \o Caching FTP + \o Implemented using an FTP proxy, it is useful only in the + context of FTP requests (see QFtp, + QNetworkAccessManager) + \o CachingCapability, HostNameLookupCapability + + \endtable + + Also note that you shouldn't set the application default proxy + (setApplicationProxy()) to a proxy that doesn't have the + TunnelingCapability capability. If you do, QTcpSocket will not + know how to open connections. + + \sa setType(), type(), capabilities(), setCapabilities() +*/ + +/*! + \enum QNetworkProxy::Capability + \since 4.5 + + These flags indicate the capabilities that a given proxy server + supports. + + QNetworkProxy sets different capabilities by default when the + object is created (see QNetworkProxy::ProxyType for a list of the + defaults). However, it is possible to change the capabitilies + after the object has been created with setCapabilities(). + + The capabilities that QNetworkProxy supports are: + + \value TunnelingCapability Ability to open transparent, tunneled + TCP connections to a remote host. The proxy server relays the + transmission verbatim from one side to the other and does no + caching. + + \value ListeningCapability Ability to create a listening socket + and wait for an incoming TCP connection from a remote host. + + \value UdpTunnelingCapability Ability to relay UDP datagrams via + the proxy server to and from a remote host. + + \value CachingCapability Ability to cache the contents of the + transfer. This capability is specific to each protocol and proxy + type. For example, HTTP proxies can cache the contents of web data + transferred with "GET" commands. + + \value HostNameLookupCapability Ability to connect to perform the + lookup on a remote host name and connect to it, as opposed to + requiring the application to perform the name lookup and request + connection to IP addresses only. +*/ + +#include "qnetworkproxy.h" + +#ifndef QT_NO_NETWORKPROXY + +#include "private/qsocks5socketengine_p.h" +#include "private/qhttpsocketengine_p.h" +#include "qauthenticator.h" +#include "qhash.h" +#include "qmutex.h" +#include "qurl.h" + +QT_BEGIN_NAMESPACE + +class QSocks5SocketEngineHandler; +class QHttpSocketEngineHandler; + +class QGlobalNetworkProxy +{ +public: + QGlobalNetworkProxy() + : mutex(QMutex::Recursive) + , applicationLevelProxy(0) + , applicationLevelProxyFactory(0) + , socks5SocketEngineHandler(0) + , httpSocketEngineHandler(0) + { + } + + ~QGlobalNetworkProxy() + { + delete applicationLevelProxy; + delete applicationLevelProxyFactory; + delete socks5SocketEngineHandler; + delete httpSocketEngineHandler; + } + + void init() + { + QMutexLocker lock(&mutex); +#ifndef QT_NO_SOCKS5 + if (!socks5SocketEngineHandler) + socks5SocketEngineHandler = new QSocks5SocketEngineHandler(); +#endif +#ifndef QT_NO_HTTP + if (!httpSocketEngineHandler) + httpSocketEngineHandler = new QHttpSocketEngineHandler(); +#endif + } + + void setApplicationProxy(const QNetworkProxy &proxy) + { + QMutexLocker lock(&mutex); + if (!applicationLevelProxy) + applicationLevelProxy = new QNetworkProxy; + *applicationLevelProxy = proxy; + delete applicationLevelProxyFactory; + applicationLevelProxyFactory = 0; + } + + void setApplicationProxyFactory(QNetworkProxyFactory *factory) + { + QMutexLocker lock(&mutex); + if (applicationLevelProxy) + *applicationLevelProxy = QNetworkProxy(); + delete applicationLevelProxyFactory; + applicationLevelProxyFactory = factory; + } + + QNetworkProxy applicationProxy() + { + return proxyForQuery(QNetworkProxyQuery()).first(); + } + + QList<QNetworkProxy> proxyForQuery(const QNetworkProxyQuery &query); + +private: + QMutex mutex; + QNetworkProxy *applicationLevelProxy; + QNetworkProxyFactory *applicationLevelProxyFactory; + QSocks5SocketEngineHandler *socks5SocketEngineHandler; + QHttpSocketEngineHandler *httpSocketEngineHandler; +}; + +QList<QNetworkProxy> QGlobalNetworkProxy::proxyForQuery(const QNetworkProxyQuery &query) +{ + QMutexLocker locker(&mutex); + + QList<QNetworkProxy> result; + if (!applicationLevelProxyFactory) { + if (applicationLevelProxy + && applicationLevelProxy->type() != QNetworkProxy::DefaultProxy) + result << *applicationLevelProxy; + else + result << QNetworkProxy(QNetworkProxy::NoProxy); + return result; + } + + // we have a factory + result = applicationLevelProxyFactory->queryProxy(query); + if (result.isEmpty()) { + qWarning("QNetworkProxyFactory: factory %p has returned an empty result set", + applicationLevelProxyFactory); + result << QNetworkProxy(QNetworkProxy::NoProxy); + } + return result; +} + +Q_GLOBAL_STATIC(QGlobalNetworkProxy, globalNetworkProxy); + +namespace { + template<bool> struct StaticAssertTest; + template<> struct StaticAssertTest<true> { enum { Value = 1 }; }; +} + +static inline void qt_noop_with_arg(int) {} +#define q_static_assert(expr) qt_noop_with_arg(sizeof(StaticAssertTest< expr >::Value)) + +static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::ProxyType type) +{ + q_static_assert(int(QNetworkProxy::DefaultProxy) == 0); + q_static_assert(int(QNetworkProxy::FtpCachingProxy) == 5); + static const int defaults[] = + { + /* [QNetworkProxy::DefaultProxy] = */ + (int(QNetworkProxy::ListeningCapability) | + int(QNetworkProxy::TunnelingCapability) | + int(QNetworkProxy::UdpTunnelingCapability)), + /* [QNetworkProxy::Socks5Proxy] = */ + (int(QNetworkProxy::TunnelingCapability) | + int(QNetworkProxy::ListeningCapability) | + int(QNetworkProxy::UdpTunnelingCapability) | + int(QNetworkProxy::HostNameLookupCapability)), + // it's weird to talk about the proxy capabilities of a "not proxy"... + /* [QNetworkProxy::NoProxy] = */ + (int(QNetworkProxy::ListeningCapability) | + int(QNetworkProxy::TunnelingCapability) | + int(QNetworkProxy::UdpTunnelingCapability)), + /* [QNetworkProxy::HttpProxy] = */ + (int(QNetworkProxy::TunnelingCapability) | + int(QNetworkProxy::CachingCapability) | + int(QNetworkProxy::HostNameLookupCapability)), + /* [QNetworkProxy::HttpCachingProxy] = */ + (int(QNetworkProxy::CachingCapability) | + int(QNetworkProxy::HostNameLookupCapability)), + /* [QNetworkProxy::FtpCachingProxy] = */ + (int(QNetworkProxy::CachingCapability) | + int(QNetworkProxy::HostNameLookupCapability)), + }; + + Q_ASSERT(int(type) >= 0 && int(type) <= int(QNetworkProxy::FtpCachingProxy)); + return QNetworkProxy::Capabilities(defaults[int(type)]); +} + +class QNetworkProxyPrivate: public QSharedData +{ +public: + QString hostName; + QString user; + QString password; + QNetworkProxy::Capabilities capabilities; + quint16 port; + QNetworkProxy::ProxyType type; + + inline QNetworkProxyPrivate(QNetworkProxy::ProxyType t = QNetworkProxy::DefaultProxy, + const QString &h = QString(), quint16 p = 0, + const QString &u = QString(), const QString &pw = QString()) + : hostName(h), + user(u), + password(pw), + capabilities(defaultCapabilitiesForType(t)), + port(p), + type(t) + { } + + inline bool operator==(const QNetworkProxyPrivate &other) const + { + return type == other.type && + port == other.port && + hostName == other.hostName && + user == other.user && + password == other.password && + capabilities == other.capabilities; + } +}; + +template<> void QSharedDataPointer<QNetworkProxyPrivate>::detach() +{ + if (d && d->ref == 1) + return; + QNetworkProxyPrivate *x = (d ? new QNetworkProxyPrivate(*d) + : new QNetworkProxyPrivate); + x->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = x; +} + +/*! + Constructs a QNetworkProxy with DefaultProxy type; the proxy type is + determined by applicationProxy(), which defaults to NoProxy. + + \sa setType(), setApplicationProxy() +*/ +QNetworkProxy::QNetworkProxy() + : d(0) +{ + globalNetworkProxy()->init(); +} + +/*! + Constructs a QNetworkProxy with \a type, \a hostName, \a port, + \a user and \a password. + + The default capabilities for proxy type \a type are set automatically. + + \sa capabilities() +*/ +QNetworkProxy::QNetworkProxy(ProxyType type, const QString &hostName, quint16 port, + const QString &user, const QString &password) + : d(new QNetworkProxyPrivate(type, hostName, port, user, password)) +{ + globalNetworkProxy()->init(); +} + +/*! + Constructs a copy of \a other. +*/ +QNetworkProxy::QNetworkProxy(const QNetworkProxy &other) + : d(other.d) +{ +} + +/*! + Destroys the QNetworkProxy object. +*/ +QNetworkProxy::~QNetworkProxy() +{ + // QSharedDataPointer takes care of deleting for us +} + +/*! + \since 4.4 + + Compares the value of this network proxy to \a other and returns true + if they are equal (same proxy type, server as well as username and password) +*/ +bool QNetworkProxy::operator==(const QNetworkProxy &other) const +{ + return d == other.d || (d && other.d && *d == *other.d); +} + +/*! + \fn bool QNetworkProxy::operator!=(const QNetworkProxy &other) const + \since 4.4 + + Compares the value of this network proxy to \a other and returns true + if they differ. +\*/ + +/*! + \since 4.2 + + Assigns the value of the network proxy \a other to this network proxy. +*/ +QNetworkProxy &QNetworkProxy::operator=(const QNetworkProxy &other) +{ + d = other.d; + return *this; +} + +/*! + Sets the proxy type for this instance to be \a type. + + Note that changing the type of a proxy does not change + the set of capabilities this QNetworkProxy object holds. + + \sa type(), setCapabilities() +*/ +void QNetworkProxy::setType(QNetworkProxy::ProxyType type) +{ + d->type = type; +} + +/*! + Returns the proxy type for this instance. + + \sa setType() +*/ +QNetworkProxy::ProxyType QNetworkProxy::type() const +{ + return d ? d->type : DefaultProxy; +} + +/*! + \since 4.5 + + Sets the capabilities of this proxy to \a capabilities. + + \sa setType(), capabilities() +*/ +void QNetworkProxy::setCapabilities(Capabilities capabilities) +{ + d->capabilities = capabilities; +} + +/*! + \since 4.5 + + Returns the capabilities of this proxy server. + + \sa setCapabilities(), type() +*/ +QNetworkProxy::Capabilities QNetworkProxy::capabilities() const +{ + return d ? d->capabilities : defaultCapabilitiesForType(DefaultProxy); +} + +/*! + \since 4.4 + + Returns true if this proxy supports the + QNetworkProxy::CachingCapability capability. + + In Qt 4.4, the capability was tied to the proxy type, but since Qt + 4.5 it is possible to remove the capability of caching from a + proxy by calling setCapabilities(). + + \sa capabilities(), type(), isTransparentProxy() +*/ +bool QNetworkProxy::isCachingProxy() const +{ + return capabilities() & CachingCapability; +} + +/*! + \since 4.4 + + Returns true if this proxy supports transparent tunneling of TCP + connections. This matches the QNetworkProxy::TunnelingCapability + capability. + + In Qt 4.4, the capability was tied to the proxy type, but since Qt + 4.5 it is possible to remove the capability of caching from a + proxy by calling setCapabilities(). + + \sa capabilities(), type(), isCachingProxy() +*/ +bool QNetworkProxy::isTransparentProxy() const +{ + return capabilities() & TunnelingCapability; +} + +/*! + Sets the user name for proxy authentication to be \a user. + + \sa user(), setPassword(), password() +*/ +void QNetworkProxy::setUser(const QString &user) +{ + d->user = user; +} + +/*! + Returns the user name used for authentication. + + \sa setUser(), setPassword(), password() +*/ +QString QNetworkProxy::user() const +{ + return d ? d->user : QString(); +} + +/*! + Sets the password for proxy authentication to be \a password. + + \sa user(), setUser(), password() +*/ +void QNetworkProxy::setPassword(const QString &password) +{ + d->password = password; +} + +/*! + Returns the password used for authentication. + + \sa user(), setPassword(), setUser() +*/ +QString QNetworkProxy::password() const +{ + return d ? d->password : QString(); +} + +/*! + Sets the host name of the proxy host to be \a hostName. + + \sa hostName(), setPort(), port() +*/ +void QNetworkProxy::setHostName(const QString &hostName) +{ + d->hostName = hostName; +} + +/*! + Returns the host name of the proxy host. + + \sa setHostName(), setPort(), port() +*/ +QString QNetworkProxy::hostName() const +{ + return d ? d->hostName : QString(); +} + +/*! + Sets the port of the proxy host to be \a port. + + \sa hostName(), setHostName(), port() +*/ +void QNetworkProxy::setPort(quint16 port) +{ + d->port = port; +} + +/*! + Returns the port of the proxy host. + + \sa setHostName(), setPort(), hostName() +*/ +quint16 QNetworkProxy::port() const +{ + return d ? d->port : 0; +} + +/*! + Sets the application level network proxying to be \a networkProxy. + + If a QAbstractSocket or QTcpSocket has the + QNetworkProxy::DefaultProxy type, then the QNetworkProxy set with + this function is used. If you want more flexibility in determining + which the proxy, use the QNetworkProxyFactory class. + + Setting a default proxy value with this function will override the + application proxy factory set with + QNetworkProxyFactory::setApplicationProxyFactory. + + \sa QNetworkProxyFactory, applicationProxy(), QAbstractSocket::setProxy(), QTcpServer::setProxy() +*/ +void QNetworkProxy::setApplicationProxy(const QNetworkProxy &networkProxy) +{ + if (globalNetworkProxy()) { + // don't accept setting the proxy to DefaultProxy + if (networkProxy.type() == DefaultProxy) + globalNetworkProxy()->setApplicationProxy(QNetworkProxy::NoProxy); + else + globalNetworkProxy()->setApplicationProxy(networkProxy); + } +} + +/*! + Returns the application level network proxying. + + If a QAbstractSocket or QTcpSocket has the + QNetworkProxy::DefaultProxy type, then the QNetworkProxy returned + by this function is used. + + \sa QNetworkProxyFactory, setApplicationProxy(), QAbstractSocket::proxy(), QTcpServer::proxy() +*/ +QNetworkProxy QNetworkProxy::applicationProxy() +{ + if (globalNetworkProxy()) + return globalNetworkProxy()->applicationProxy(); + return QNetworkProxy(); +} + +class QNetworkProxyQueryPrivate: public QSharedData +{ +public: + inline QNetworkProxyQueryPrivate() + : localPort(-1), type(QNetworkProxyQuery::TcpSocket) + { } + + bool operator==(const QNetworkProxyQueryPrivate &other) const + { + return type == other.type && + localPort == other.localPort && + remote == other.remote; + } + + QUrl remote; + int localPort; + QNetworkProxyQuery::QueryType type; +}; + +template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach() +{ + if (d && d->ref == 1) + return; + QNetworkProxyQueryPrivate *x = (d ? new QNetworkProxyQueryPrivate(*d) + : new QNetworkProxyQueryPrivate); + x->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = x; +} + +/*! + \class QNetworkProxyQuery + \since 4.5 + \inmodule QtNetwork + \brief The QNetworkProxyQuery class is used to query the proxy + settings for a socket + + QNetworkProxyQuery holds the details of a socket being created or + request being made. It is used by QNetworkProxy and + QNetworkProxyFactory to allow applications to have a more + fine-grained control over which proxy servers are used, depending + on the details of the query. This allows an application to apply + different settings, according to the protocol or destination + hostname, for instance. + + QNetworkProxyQuery supports the following criteria for selecting + the proxy: + + \list + \o the type of query + \o the local port number to use + \o the destination host name + \o the destination port number + \o the protocol name, such as "http" or "ftp" + \o the URL being requested + \endlist + + The destination host name is the host in the connection in the + case of outgoing connection sockets. It is the \c hostName + parameter passed to QTcpSocket::connectToHost() or the host + component of a URL requested with QNetworkRequest. + + The destination port number is the requested port to connect to in + the case of outgoing sockets, while the local port number is the + port the socket wishes to use locally before attempting the + external connection. In most cases, the local port number is used + by listening sockets only (QTcpSocket) or by datagram sockets + (QUdpSocket). + + The protocol name is an arbitrary string that indicates the type + of connection being attempted. For example, it can match the + scheme of a URL, like "http", "https" and "ftp". In most cases, + the proxy selection will not change depending on the protocol, but + this information is provided in case a better choice can be made, + like choosing an caching HTTP proxy for HTTP-based connections, + but a more powerful SOCKSv5 proxy for all others. + + Some of the criteria may not make sense in all of the types of + query. The following table lists the criteria that are most + commonly used, according to the type of query. + + \table + \header + \o Query type + \o Description + + \row + \o TcpSocket + \o Normal sockets requesting a connection to a remote server, + like QTcpSocket. The peer hostname and peer port match the + values passed to QTcpSocket::connectToHost(). The local port + is usually -1, indicating the socket has no preference in + which port should be used. The URL component is not used. + + \row + \o UdpSocket + \o Datagram-based sockets, which can both send and + receive. The local port, remote host or remote port fields + can all be used or be left unused, depending on the + characteristics of the socket. The URL component is not used. + + \row + \o TcpServer + \o Passive server sockets that listen on a port and await + incoming connections from the network. Normally, only the + local port is used, but the remote address could be used in + specific circumstances, for example to indicate which remote + host a connection is expected from. The URL component is not used. + + \row + \o UrlRequest + \o A more high-level request, such as those coming from + QNetworkAccessManager. These requests will inevitably use an + outgoing TCP socket, but the this query type is provided to + indicate that more detailed information is present in the URL + component. For ease of implementation, the URL's host and + port are set as the destination address. + \endtable + + It should be noted that any of the criteria may be missing or + unknown (an empty QString for the hostname or protocol name, -1 + for the port numbers). If that happens, the functions executing + the query should make their best guess or apply some + implementation-defined default values. + + \sa QNetworkProxy, QNetworkProxyFactory, QNetworkAccessManager, + QAbstractSocket::setProxy() +*/ + +/*! + \enum QNetworkProxyQuery::QueryType + + Describes the type of one QNetworkProxyQuery query. + + \value TcpSocket a normal, outgoing TCP socket + \value UdpSocket a datagram-based UDP socket, which could send + to multiple destinations + \value TcpServer a TCP server that listens for incoming + connections from the network + \value UrlRequest a more complex request which involves loading + of a URL + + \sa queryType(), setQueryType() +*/ + +/*! + Constructs a default QNetworkProxyQuery object. By default, the + query type will be QNetworkProxyQuery::TcpSocket. +*/ +QNetworkProxyQuery::QNetworkProxyQuery() +{ +} + +/*! + Constructs a QNetworkProxyQuery with the URL \a requestUrl and + sets the query type to \a queryType. + + \sa protocolTag(), peerHostName(), peerPort() +*/ +QNetworkProxyQuery::QNetworkProxyQuery(const QUrl &requestUrl, QueryType queryType) +{ + d->remote = requestUrl; + d->type = queryType; +} + +/*! + Constructs a QNetworkProxyQuery of type \a queryType and sets the + protocol tag to be \a protocolTag. This constructor is suitable + for QNetworkProxyQuery::TcpSocket queries, because it sets the + peer hostname to \a hostname and the peer's port number to \a + port. +*/ +QNetworkProxyQuery::QNetworkProxyQuery(const QString &hostname, int port, + const QString &protocolTag, + QueryType queryType) +{ + d->remote.setScheme(protocolTag); + d->remote.setHost(hostname); + d->remote.setPort(port); + d->type = queryType; +} + +/*! + Constructs a QNetworkProxyQuery of type \a queryType and sets the + protocol tag to be \a protocolTag. This constructor is suitable + for QNetworkProxyQuery::TcpSocket queries because it sets the + local port number to \a bindPort. + + Note that \a bindPort is of type quint16 to indicate the exact + port number that is requested. The value of -1 (unknown) is not + allowed in this context. + + \sa localPort() +*/ +QNetworkProxyQuery::QNetworkProxyQuery(quint16 bindPort, const QString &protocolTag, + QueryType queryType) +{ + d->remote.setScheme(protocolTag); + d->localPort = bindPort; + d->type = queryType; +} + +/*! + Constructs a QNetworkProxyQuery object that is a copy of \a other. +*/ +QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkProxyQuery &other) + : d(other.d) +{ +} + +/*! + Destroys this QNetworkProxyQuery object. +*/ +QNetworkProxyQuery::~QNetworkProxyQuery() +{ + // QSharedDataPointer automatically deletes +} + +/*! + Copies the contents of \a other. +*/ +QNetworkProxyQuery &QNetworkProxyQuery::operator=(const QNetworkProxyQuery &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this QNetworkProxyQuery object contains the same + data as \a other. +*/ +bool QNetworkProxyQuery::operator==(const QNetworkProxyQuery &other) const +{ + return d == other.d || (d && other.d && *d == *other.d); +} + +/*! + \fn bool QNetworkProxyQuery::operator!=(const QNetworkProxyQuery &other) const + + Returns true if this QNetworkProxyQuery object does not contain + the same data as \a other. +*/ + +/*! + Returns the query type. +*/ +QNetworkProxyQuery::QueryType QNetworkProxyQuery::queryType() const +{ + return d ? d->type : TcpSocket; +} + +/*! + Sets the query type of this object to be \a type. +*/ +void QNetworkProxyQuery::setQueryType(QueryType type) +{ + d->type = type; +} + +/*! + Returns the port number for the outgoing request or -1 if the port + number is not known. + + If the query type is QNetworkProxyQuery::UrlRequest, this function + returns the port number of the URL being requested. In general, + frameworks will fill in the port number from their default values. + + \sa peerHostName(), localPort(), setPeerPort() +*/ +int QNetworkProxyQuery::peerPort() const +{ + return d ? d->remote.port() : -1; +} + +/*! + Sets the requested port number for the outgoing connection to be + \a port. Valid values are 1 to 65535, or -1 to indicate that the + remote port number is unknown. + + The peer port number can also be used to indicate the expected + port number of an incoming connection in the case of + QNetworkProxyQuery::UdpSocket or QNetworkProxyQuery::TcpServer + query types. + + \sa peerPort(), setPeerHostName(), setLocalPort() +*/ +void QNetworkProxyQuery::setPeerPort(int port) +{ + d->remote.setPort(port); +} + +/*! + Returns the host name or IP address being of the outgoing + connection being requested, or an empty string if the remote + hostname is not known. + + If the query type is QNetworkProxyQuery::UrlRequest, this function + returns the host component of the URL being requested. + + \sa peerPort(), localPort(), setPeerHostName() +*/ +QString QNetworkProxyQuery::peerHostName() const +{ + return d ? d->remote.host() : QString(); +} + +/*! + Sets the hostname of the outgoing connection being requested to \a + hostname. An empty hostname can be used to indicate that the + remote host is unknown. + + The peer host name can also be used to indicate the expected + source address of an incoming connection in the case of + QNetworkProxyQuery::UdpSocket or QNetworkProxyQuery::TcpServer + query types. + + \sa peerHostName(), setPeerPort(), setLocalPort() +*/ +void QNetworkProxyQuery::setPeerHostName(const QString &hostname) +{ + d->remote.setHost(hostname); +} + +/*! + Returns the port number of the socket that will accept incoming + packets from remote servers or -1 if the port is not known. + + \sa peerPort(), peerHostName(), setLocalPort() +*/ +int QNetworkProxyQuery::localPort() const +{ + return d ? d->localPort : -1; +} + +/*! + Sets the port number that the socket wishes to use locally to + accept incoming packets from remote servers to \a port. The local + port is most often used with the QNetworkProxyQuery::TcpServer + and QNetworkProxyQuery::UdpSocket query types. + + Valid values are 0 to 65535 (with 0 indicating that any port + number will be acceptable) or -1, which means the local port + number is unknown or not applicable. + + In some circumstances, for special protocols, it's the local port + number can also be used with a query of type + QNetworkProxyQuery::TcpSocket. When that happens, the socket is + indicating it wishes to use the port number \a port when + connecting to a remote host. + + \sa localPort(), setPeerPort(), setPeerHostName() +*/ +void QNetworkProxyQuery::setLocalPort(int port) +{ + d->localPort = port; +} + +/*! + Returns the protocol tag for this QNetworkProxyQuery object, or an + empty QString in case the protocol tag is unknown. + + In the case of queries of type QNetworkProxyQuery::UrlRequest, + this function returns the value of the scheme component of the + URL. + + \sa setProtocolTag(), url() +*/ +QString QNetworkProxyQuery::protocolTag() const +{ + return d ? d->remote.scheme() : QString(); +} + +/*! + Sets the protocol tag for this QNetworkProxyQuery object to be \a + protocolTag. + + The protocol tag is an arbitrary string that indicates which + protocol is being talked over the socket, such as "http", "xmpp", + "telnet", etc. The protocol tag is used by the backend to + return a request that is more specific to the protocol in + question: for example, a HTTP connection could be use a caching + HTTP proxy server, while all other connections use a more powerful + SOCKSv5 proxy server. + + \sa protocolTag() +*/ +void QNetworkProxyQuery::setProtocolTag(const QString &protocolTag) +{ + d->remote.setScheme(protocolTag); +} + +/*! + Returns the URL component of this QNetworkProxyQuery object in + case of a query of type QNetworkProxyQuery::UrlRequest. + + \sa setUrl() +*/ +QUrl QNetworkProxyQuery::url() const +{ + return d ? d->remote : QUrl(); +} + +/*! + Sets the URL component of this QNetworkProxyQuery object to be \a + url. Setting the URL will also set the protocol tag, the remote + host name and port number. This is done so as to facilitate the + implementation of the code that determines the proxy server to be + used. + + \sa url(), peerHostName(), peerPort() +*/ +void QNetworkProxyQuery::setUrl(const QUrl &url) +{ + d->remote = url; +} + +/*! + \class QNetworkProxyFactory + \brief The QNetworkProxyFactory class provides fine-grained proxy selection. + \since 4.5 + + \ingroup io + \inmodule QtNetwork + + QNetworkProxyFactory is an extension to QNetworkProxy, allowing + applications to have a more fine-grained control over which proxy + servers are used, depending on the socket requesting the + proxy. This allows an application to apply different settings, + according to the protocol or destination hostname, for instance. + + QNetworkProxyFactory can be set globally for an application, in + which case it will override any global proxies set with + QNetworkProxy::setApplicationProxy(). If set globally, any sockets + created with Qt will query the factory to determine the proxy to + be used. + + A factory can also be set in certain frameworks that support + multiple connections, such as QNetworkAccessManager. When set on + such object, the factory will be queried for sockets created by + that framework only. +*/ + +/*! + Creates a QNetworkProxyFactory object. + + Since QNetworkProxyFactory is an abstract class, you cannot create + objects of type QNetworkProxyFactory directly. +*/ +QNetworkProxyFactory::QNetworkProxyFactory() +{ +} + +/*! + Destroys the QNetworkProxyFactory object. +*/ +QNetworkProxyFactory::~QNetworkProxyFactory() +{ +} + +/*! + Sets the application-wide proxy factory to be \a factory. This + function will take ownership of that object and will delete it + when necessary. + + The application-wide proxy is used as a last-resort when all other + proxy selection requests returned QNetworkProxy::DefaultProxy. For + example, QTcpSocket objects can have a proxy set with + QTcpSocket::setProxy, but if none is set, the proxy factory class + set with this function will be queried. + + If you set a proxy factory with this function, any application + level proxies set with QNetworkProxy::setApplicationProxy will be + overridden. + + \sa QNetworkProxy::setApplicationProxy(), + QAbstractSocket::proxy(), QAbstractSocket::setProxy() +*/ +void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *factory) +{ + if (globalNetworkProxy()) + globalNetworkProxy()->setApplicationProxyFactory(factory); +} + +/*! + \fn QList<QNetworkProxy> QNetworkProxyFactory::queryProxy(const QNetworkProxyQuery &query) + + This function examines takes the query request, \a query, + examines the details of the type of socket or request and returns + a list of QNetworkProxy objects that indicate the proxy servers to + be used, in order of preference. + + When reimplementing this class, take care to return at least one + element. + + If you cannot determine a better proxy alternative, use + QNetworkProxy::DefaultProxy, which tells the code querying for a + proxy to use a higher alternative. For example, if this factory is + set to a QNetworkAccessManager object, DefaultProxy will tell it + to query the application-level proxy settings. + + If this factory is set as the application proxy factory, + DefaultProxy and NoProxy will have the same meaning. +*/ + +/*! + \fn QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query) + + This function examines takes the query request, \a query, + examines the details of the type of socket or request and returns + a list of QNetworkProxy objects that indicate the proxy servers to + be used, in order of preference. + + This function can be used to determine the platform-specific proxy + settings. This function will use the libraries provided by the + operating system to determine the proxy for a given connection, if + such libraries exist. If they don't, this function will just return a + QNetworkProxy of type QNetworkProxy::NoProxy. + + On Windows, this function will use the WinHTTP DLL functions. Despite + its name, Microsoft suggests using it for all applications that + require network connections, not just HTTP. This will respect the + proxy settings set on the registry with the proxycfg.exe tool. If + those settings are not found, this function will attempt to obtain + Internet Explorer's settings and use them. + + On MacOS X, this function will obtain the proxy settings using the + SystemConfiguration framework from Apple. It will apply the FTP, + HTTP and HTTPS proxy configurations for queries that contain the + protocol tag "ftp", "http" and "https", respectively. If the SOCKS + proxy is enabled in that configuration, this function will use the + SOCKS server for all queries. If SOCKS isn't enabled, it will use + the HTTPS proxy for all TcpSocket and UrlRequest queries. + + On other systems, there is no standardised method of obtaining the + system proxy configuration. This function may be improved in + future versions to support those systems. + + \section1 Limitations + + These are the limitations for the current version of this + function. Future versions of Qt may lift some of the limitations + listed here. + + On MacOS X, this function will ignore the Proxy Auto Configuration + settings, since it cannot execute the associated ECMAScript code. +*/ + +/*! + This function examines takes the query request, \a query, + examines the details of the type of socket or request and returns + a list of QNetworkProxy objects that indicate the proxy servers to + be used, in order of preference. +*/ +QList<QNetworkProxy> QNetworkProxyFactory::proxyForQuery(const QNetworkProxyQuery &query) +{ + if (!globalNetworkProxy()) + return QList<QNetworkProxy>() << QNetworkProxy(QNetworkProxy::NoProxy); + return globalNetworkProxy()->proxyForQuery(query); +} + +#endif // QT_NO_NETWORKPROXY + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h new file mode 100644 index 0000000..abe4213 --- /dev/null +++ b/src/network/kernel/qnetworkproxy.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKPROXY_H +#define QNETWORKPROXY_H + +#include <QtNetwork/qhostaddress.h> +#include <QtCore/qshareddata.h> + +#ifndef QT_NO_NETWORKPROXY + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +class QUrl; + +class QNetworkProxyQueryPrivate; +class Q_NETWORK_EXPORT QNetworkProxyQuery +{ +public: + enum QueryType { + TcpSocket, + UdpSocket, + TcpServer = 100, + UrlRequest + }; + + QNetworkProxyQuery(); + QNetworkProxyQuery(const QUrl &requestUrl, QueryType queryType = UrlRequest); + QNetworkProxyQuery(const QString &hostname, int port, const QString &protocolTag = QString(), + QueryType queryType = TcpSocket); + QNetworkProxyQuery(quint16 bindPort, const QString &protocolTag = QString(), + QueryType queryType = TcpServer); + QNetworkProxyQuery(const QNetworkProxyQuery &other); + ~QNetworkProxyQuery(); + QNetworkProxyQuery &operator=(const QNetworkProxyQuery &other); + bool operator==(const QNetworkProxyQuery &other) const; + inline bool operator!=(const QNetworkProxyQuery &other) const + { return !(*this == other); } + + QueryType queryType() const; + void setQueryType(QueryType type); + + int peerPort() const; + void setPeerPort(int port); + + QString peerHostName() const; + void setPeerHostName(const QString &hostname); + + int localPort() const; + void setLocalPort(int port); + + QString protocolTag() const; + void setProtocolTag(const QString &protocolTag); + + QUrl url() const; + void setUrl(const QUrl &url); + +private: + QSharedDataPointer<QNetworkProxyQueryPrivate> d; +}; +Q_DECLARE_TYPEINFO(QNetworkProxyQuery, Q_MOVABLE_TYPE); + +class QNetworkProxyPrivate; + +class Q_NETWORK_EXPORT QNetworkProxy +{ +public: + enum ProxyType { + DefaultProxy, + Socks5Proxy, + NoProxy, + HttpProxy, + HttpCachingProxy, + FtpCachingProxy + }; + + enum Capability { + TunnelingCapability = 0x0001, + ListeningCapability = 0x0002, + UdpTunnelingCapability = 0x0004, + CachingCapability = 0x0008, + HostNameLookupCapability = 0x0010 + }; + Q_DECLARE_FLAGS(Capabilities, Capability) + + QNetworkProxy(); + QNetworkProxy(ProxyType type, const QString &hostName = QString(), quint16 port = 0, + const QString &user = QString(), const QString &password = QString()); + QNetworkProxy(const QNetworkProxy &other); + QNetworkProxy &operator=(const QNetworkProxy &other); + ~QNetworkProxy(); + bool operator==(const QNetworkProxy &other) const; + inline bool operator!=(const QNetworkProxy &other) const + { return !(*this == other); } + + void setType(QNetworkProxy::ProxyType type); + QNetworkProxy::ProxyType type() const; + + void setCapabilities(Capabilities capab); + Capabilities capabilities() const; + bool isCachingProxy() const; + bool isTransparentProxy() const; + + void setUser(const QString &userName); + QString user() const; + + void setPassword(const QString &password); + QString password() const; + + void setHostName(const QString &hostName); + QString hostName() const; + + void setPort(quint16 port); + quint16 port() const; + + static void setApplicationProxy(const QNetworkProxy &proxy); + static QNetworkProxy applicationProxy(); + +private: + QSharedDataPointer<QNetworkProxyPrivate> d; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkProxy::Capabilities) + +class Q_NETWORK_EXPORT QNetworkProxyFactory +{ +public: + QNetworkProxyFactory(); + virtual ~QNetworkProxyFactory(); + + virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery()) = 0; + + static void setApplicationProxyFactory(QNetworkProxyFactory *factory); + static QList<QNetworkProxy> proxyForQuery(const QNetworkProxyQuery &query); + static QList<QNetworkProxy> systemProxyForQuery(const QNetworkProxyQuery &query = QNetworkProxyQuery()); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_NETWORKPROXY + +#endif // QHOSTINFO_H diff --git a/src/network/kernel/qnetworkproxy_generic.cpp b/src/network/kernel/qnetworkproxy_generic.cpp new file mode 100644 index 0000000..866d63d --- /dev/null +++ b/src/network/kernel/qnetworkproxy_generic.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkproxy.h" + +#ifndef QT_NO_NETWORKPROXY + +/* + * No system proxy. Just return a list with NoProxy. + */ + +QT_BEGIN_NAMESPACE + +QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &) +{ + return QList<QNetworkProxy>() << QNetworkProxy::NoProxy; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp new file mode 100644 index 0000000..5e6e3e8 --- /dev/null +++ b/src/network/kernel/qnetworkproxy_mac.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkproxy.h" + +#ifndef QT_NO_NETWORKPROXY + +#include <CoreFoundation/CoreFoundation.h> +#include <SystemConfiguration/SystemConfiguration.h> + +#include <QtCore/QRegExp> +#include <QtCore/QStringList> +#include <QtCore/qendian.h> +#include <QtCore/qstringlist.h> +#include "private/qcore_mac_p.h" + +/* + * MacOS X has a proxy configuration module in System Preferences (on + * MacOS X 10.5, it's in Network, Advanced), where one can set the + * proxy settings for: + * + * \list + * \o FTP proxy + * \o Web Proxy (HTTP) + * \o Secure Web Proxy (HTTPS) + * \o Streaming Proxy (RTSP) + * \o SOCKS Proxy + * \o Gopher Proxy + * \o URL for Automatic Proxy Configuration (PAC scripts) + * \o Bypass list (by default: *.local, 169.254/16) + * \endlist + * + * The matching configuration can be obtained by calling SCDynamicStoreCopyProxies + * (from <SystemConfiguration/SCDynamicStoreCopySpecific.h>). See + * Apple's documentation: + * + * http://developer.apple.com/DOCUMENTATION/Networking/Reference/SysConfig/SCDynamicStoreCopySpecific/CompositePage.html#//apple_ref/c/func/SCDynamicStoreCopyProxies + * + */ + +QT_BEGIN_NAMESPACE + +static bool isHostExcluded(CFDictionaryRef dict, const QString &host) +{ + if (host.isEmpty()) + return true; + + bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':')); + CFNumberRef excludeSimples; + if (isSimple && + (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExcludeSimpleHostnames))) { + int enabled; + if (CFNumberGetValue(excludeSimples, kCFNumberIntType, &enabled) && enabled) + return true; + } + + QHostAddress ipAddress; + bool isIpAddress = ipAddress.setAddress(host); + + // not a simple host name + // does it match the list of exclusions? + CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList); + if (!exclusionList) + return false; + + CFIndex size = CFArrayGetCount(exclusionList); + for (CFIndex i = 0; i < size; ++i) { + CFStringRef cfentry = (CFStringRef)CFArrayGetValueAtIndex(exclusionList, i); + QString entry = QCFString::toQString(cfentry); + + if (isIpAddress && ipAddress.isInSubnet(QHostAddress::parseSubnet(entry))) { + return true; // excluded + } else { + // do wildcard matching + QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard); + if (rx.exactMatch(host)) + return true; + } + } + + // host was not excluded + return false; +} + +static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict, QNetworkProxy::ProxyType type, + CFStringRef enableKey, CFStringRef hostKey, + CFStringRef portKey) +{ + CFNumberRef protoEnabled; + CFNumberRef protoPort; + CFStringRef protoHost; + if (enableKey + && (protoEnabled = (CFNumberRef)CFDictionaryGetValue(dict, enableKey)) + && (protoHost = (CFStringRef)CFDictionaryGetValue(dict, hostKey)) + && (protoPort = (CFNumberRef)CFDictionaryGetValue(dict, portKey))) { + int enabled; + if (CFNumberGetValue(protoEnabled, kCFNumberIntType, &enabled) && enabled) { + QString host = QCFString::toQString(protoHost); + + int port; + CFNumberGetValue(protoPort, kCFNumberIntType, &port); + + return QNetworkProxy(type, host, port); + } + } + + // proxy not enabled + return QNetworkProxy(); +} + +QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query) +{ + QList<QNetworkProxy> result; + + // obtain a dictionary to the proxy settings: + CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + if (!dict) { + qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL"); + return result; // failed + } + + if (isHostExcluded(dict, query.peerHostName())) + return result; // no proxy for this host + + // is there a PAC enabled? If so, use it first. + CFNumberRef pacEnabled; + if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigEnable))) { + int enabled; + if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) { + // PAC is enabled + CFStringRef pacUrl = + (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString); + QString url = QCFString::toQString(pacUrl); + + // ### Use PAC somehow + qDebug("Mac system proxy: found PAC script at \"%s\"", qPrintable(url)); + } + } + + // no PAC, decide which proxy we're looking for based on the query + bool isHttps = false; + QString protocol = query.protocolTag().toLower(); + + // try the protocol-specific proxy + QNetworkProxy protocolSpecificProxy; + if (protocol == QLatin1String("ftp")) { + protocolSpecificProxy = + proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy, + kSCPropNetProxiesFTPEnable, + kSCPropNetProxiesFTPProxy, + kSCPropNetProxiesFTPPort); + } else if (protocol == QLatin1String("http")) { + protocolSpecificProxy = + proxyFromDictionary(dict, QNetworkProxy::HttpProxy, + kSCPropNetProxiesHTTPEnable, + kSCPropNetProxiesHTTPProxy, + kSCPropNetProxiesHTTPPort); + } else if (protocol == QLatin1String("https")) { + isHttps = true; + protocolSpecificProxy = + proxyFromDictionary(dict, QNetworkProxy::HttpProxy, + kSCPropNetProxiesHTTPSEnable, + kSCPropNetProxiesHTTPSProxy, + kSCPropNetProxiesHTTPSPort); + } + if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy) + result << protocolSpecificProxy; + + // let's add SOCKSv5 if present too + QNetworkProxy socks5 = proxyFromDictionary(dict, QNetworkProxy::Socks5Proxy, + kSCPropNetProxiesSOCKSEnable, + kSCPropNetProxiesSOCKSProxy, + kSCPropNetProxiesSOCKSPort); + if (socks5.type() != QNetworkProxy::DefaultProxy) + result << socks5; + + // let's add the HTTPS proxy if present (and if we haven't added + // yet) + if (!isHttps) { + QNetworkProxy https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy, + kSCPropNetProxiesHTTPSEnable, + kSCPropNetProxiesHTTPSProxy, + kSCPropNetProxiesHTTPSPort); + if (https.type() != QNetworkProxy::DefaultProxy && https != protocolSpecificProxy) + result << https; + } + + return result; +} + +QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query) +{ + QList<QNetworkProxy> result = macQueryInternal(query); + if (result.isEmpty()) + result << QNetworkProxy::NoProxy; + + return result; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp new file mode 100644 index 0000000..16d18cf --- /dev/null +++ b/src/network/kernel/qnetworkproxy_win.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkproxy.h" + +#ifndef QT_NO_NETWORKPROXY + +#if defined(UNICODE) + +#include <qmutex.h> +#include <qstringlist.h> +#include <qregexp.h> +#include <qurl.h> + +#include <string.h> +#include <windows.h> +#include <wininet.h> + +/* + * Information on the WinHTTP DLL: + * http://msdn.microsoft.com/en-us/library/aa384122(VS.85).aspx example for WPAD + * + * http://msdn.microsoft.com/en-us/library/aa384097(VS.85).aspx WinHttpGetProxyForUrl + * http://msdn.microsoft.com/en-us/library/aa384096(VS.85).aspx WinHttpGetIEProxyConfigForCurrentUs + * http://msdn.microsoft.com/en-us/library/aa384095(VS.85).aspx WinHttpGetDefaultProxyConfiguration + */ + +// We don't want to include winhttp.h because that's not +// present in some Windows SDKs (I don't know why) +// So, instead, copy the definitions here + +typedef struct { + DWORD dwFlags; + DWORD dwAutoDetectFlags; + LPCWSTR lpszAutoConfigUrl; + LPVOID lpvReserved; + DWORD dwReserved; + BOOL fAutoLogonIfChallenged; +} WINHTTP_AUTOPROXY_OPTIONS; + +typedef struct { + DWORD dwAccessType; + LPWSTR lpszProxy; + LPWSTR lpszProxyBypass; +} WINHTTP_PROXY_INFO; + +typedef struct { + BOOL fAutoDetect; + LPWSTR lpszAutoConfigUrl; + LPWSTR lpszProxy; + LPWSTR lpszProxyBypass; +} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; + +#define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001 +#define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002 + +#define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001 +#define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002 + +#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0 +#define WINHTTP_ACCESS_TYPE_NO_PROXY 1 +#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3 + +#define WINHTTP_NO_PROXY_NAME NULL +#define WINHTTP_NO_PROXY_BYPASS NULL + +QT_BEGIN_NAMESPACE + +typedef BOOL (WINAPI * PtrWinHttpGetProxyForUrl)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS*, WINHTTP_PROXY_INFO*); +typedef HINTERNET (WINAPI * PtrWinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR,DWORD); +typedef BOOL (WINAPI * PtrWinHttpGetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*); +typedef BOOL (WINAPI * PtrWinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*); +typedef BOOL (WINAPI * PtrWinHttpCloseHandle)(HINTERNET); +static PtrWinHttpGetProxyForUrl ptrWinHttpGetProxyForUrl = 0; +static PtrWinHttpOpen ptrWinHttpOpen = 0; +static PtrWinHttpGetDefaultProxyConfiguration ptrWinHttpGetDefaultProxyConfiguration = 0; +static PtrWinHttpGetIEProxyConfigForCurrentUser ptrWinHttpGetIEProxyConfigForCurrentUser = 0; +static PtrWinHttpCloseHandle ptrWinHttpCloseHandle = 0; + + +static QStringList splitSpaceSemicolon(const QString &source) +{ + QStringList list; + int start = 0; + int end; + while (true) { + int space = source.indexOf(QLatin1Char(' '), start); + int semicolon = source.indexOf(QLatin1Char(';'), start); + end = space; + if (semicolon != -1 && (end == -1 || semicolon < end)) + end = semicolon; + + if (end == -1) { + if (start != source.length()) + list.append(source.mid(start)); + return list; + } + if (start != end) + list.append(source.mid(start, end - start)); + start = end + 1; + } + return list; +} + +static bool isBypassed(const QString &host, const QStringList &bypassList) +{ + if (host.isEmpty()) + return true; + + bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':')); + + QHostAddress ipAddress; + bool isIpAddress = ipAddress.setAddress(host); + + // does it match the list of exclusions? + foreach (const QString &entry, bypassList) { + if (isSimple && entry == QLatin1String("<local>")) + return true; + if (isIpAddress && ipAddress.isInSubnet(QHostAddress::parseSubnet(entry))) { + return true; // excluded + } else { + // do wildcard matching + QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard); + if (rx.exactMatch(host)) + return true; + } + } + + // host was not excluded + return false; +} + +static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, const QStringList &proxyList) +{ + // Reference documentation from Microsoft: + // http://msdn.microsoft.com/en-us/library/aa383912(VS.85).aspx + // + // According to the website, the proxy server list is + // one or more of the space- or semicolon-separated strings in the format: + // ([<scheme>=][<scheme>"://"]<server>[":"<port>]) + + QList<QNetworkProxy> result; + foreach (const QString &entry, proxyList) { + int server = 0; + + int pos = entry.indexOf(QLatin1Char('=')); + if (pos != -1) { + QStringRef scheme = entry.leftRef(pos); + if (scheme != query.protocolTag()) + continue; + + server = pos + 1; + } + + QNetworkProxy::ProxyType proxyType = QNetworkProxy::HttpProxy; + quint16 port = 8080; + + pos = entry.indexOf(QLatin1String("://"), server); + if (pos != -1) { + QStringRef scheme = entry.midRef(server, pos - server); + if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) { + // no-op + // defaults are above + } else if (scheme == QLatin1String("socks") || scheme == QLatin1String("socks5")) { + proxyType = QNetworkProxy::Socks5Proxy; + port = 1080; + } else { + // unknown proxy type + continue; + } + + server = pos + 3; + } + + pos = entry.indexOf(QLatin1Char(':'), server); + if (pos != -1) { + bool ok; + uint value = entry.mid(pos + 1).toUInt(&ok); + if (!ok || value > 65535) + continue; // invalid port number + + port = value; + } else { + pos = entry.length(); + } + + result << QNetworkProxy(proxyType, entry.mid(server, pos - server), port); + } + + return result; +} + +class QWindowsSystemProxy +{ +public: + QWindowsSystemProxy(); + ~QWindowsSystemProxy(); + void init(); + + QMutex mutex; + + HINTERNET hHttpSession; + WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions; + + QString autoConfigUrl; + QStringList proxyServerList; + QStringList proxyBypass; + QList<QNetworkProxy> defaultResult; + + bool initialized; + bool functional; + bool isAutoConfig; +}; + +Q_GLOBAL_STATIC(QWindowsSystemProxy, systemProxy) + +QWindowsSystemProxy::QWindowsSystemProxy() + : initialized(false), functional(false), isAutoConfig(false) +{ + defaultResult << QNetworkProxy::NoProxy; +} + +QWindowsSystemProxy::~QWindowsSystemProxy() +{ + if (hHttpSession) + ptrWinHttpCloseHandle(hHttpSession); +} + +void QWindowsSystemProxy::init() +{ + if (initialized) + return; + initialized = true; + if (QSysInfo::windowsVersion() & QSysInfo::WV_DOS_based) + return; // no point, this library is only available on 2k, XP and up + +#ifdef Q_OS_WINCE + // Windows CE does not have any of the following API + return; +#else + // load the winhttp.dll library + HINSTANCE winhttpHnd = LoadLibraryW(L"winhttp"); + if (!winhttpHnd) + return; // failed to load + + ptrWinHttpOpen = (PtrWinHttpOpen)GetProcAddress(winhttpHnd, "WinHttpOpen"); + ptrWinHttpCloseHandle = (PtrWinHttpCloseHandle)GetProcAddress(winhttpHnd, "WinHttpCloseHandle"); + ptrWinHttpGetProxyForUrl = (PtrWinHttpGetProxyForUrl)GetProcAddress(winhttpHnd, "WinHttpGetProxyForUrl"); + ptrWinHttpGetDefaultProxyConfiguration = (PtrWinHttpGetDefaultProxyConfiguration)GetProcAddress(winhttpHnd, "WinHttpGetDefaultProxyConfiguration"); + ptrWinHttpGetIEProxyConfigForCurrentUser = (PtrWinHttpGetIEProxyConfigForCurrentUser)GetProcAddress(winhttpHnd, "WinHttpGetIEProxyConfigForCurrentUser"); + + // Try to obtain the Internet Explorer configuration. + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig; + if (ptrWinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig)) { + if (ieProxyConfig.lpszAutoConfigUrl) { + autoConfigUrl = QString::fromWCharArray(ieProxyConfig.lpszAutoConfigUrl); + GlobalFree(ieProxyConfig.lpszAutoConfigUrl); + } + if (ieProxyConfig.lpszProxy) { + proxyServerList << QString::fromWCharArray(ieProxyConfig.lpszProxy); + GlobalFree(ieProxyConfig.lpszProxy); + } + if (ieProxyConfig.lpszProxyBypass) { + proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxyBypass)); + GlobalFree(ieProxyConfig.lpszProxyBypass); + } + } + + hHttpSession = NULL; + if (ieProxyConfig.fAutoDetect || !autoConfigUrl.isEmpty()) { + // using proxy autoconfiguration + proxyServerList.clear(); + proxyBypass.clear(); + + // open the handle and obtain the options + hHttpSession = ptrWinHttpOpen(L"Qt System Proxy access/1.0", + WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0); + if (!hHttpSession) + return; + + isAutoConfig = true; + memset(&autoProxyOptions, 0, sizeof autoProxyOptions); + autoProxyOptions.fAutoLogonIfChallenged = true; + if (ieProxyConfig.fAutoDetect) { + autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | + WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } else { + autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; + autoProxyOptions.lpszAutoConfigUrl = (LPCWSTR)autoConfigUrl.utf16(); + } + } else { + // not auto-detected + // attempt to get the static configuration instead + WINHTTP_PROXY_INFO proxyInfo; + if (ptrWinHttpGetDefaultProxyConfiguration(&proxyInfo) && + proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { + // we got information from the registry + // overwrite the IE configuration, if any + + proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxyBypass)); + proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy)); + } + + if (proxyInfo.lpszProxy) + GlobalFree(proxyInfo.lpszProxy); + if (proxyInfo.lpszProxyBypass) + GlobalFree(proxyInfo.lpszProxyBypass); + } + + functional = isAutoConfig || !proxyServerList.isEmpty(); +#endif +} + +QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query) +{ + QWindowsSystemProxy *sp = systemProxy(); + if (!sp) + return QList<QNetworkProxy>() << QNetworkProxy(); + + QMutexLocker locker(&sp->mutex); + sp->init(); + if (!sp->functional) + return sp->defaultResult; + + if (sp->isAutoConfig) { + WINHTTP_PROXY_INFO proxyInfo; + + // try to get the proxy config for the URL, if we have a URL + QUrl url = query.url(); + if (query.queryType() != QNetworkProxyQuery::UrlRequest) { + // change the scheme to https, maybe it'll work + url.setScheme(QLatin1String("https")); + } + if (ptrWinHttpGetProxyForUrl(sp->hHttpSession, + (LPCWSTR)url.toString().utf16(), + &sp->autoProxyOptions, + &proxyInfo)) { + // yes, we got a config for this URL + QString proxyBypass = QString::fromWCharArray(proxyInfo.lpszProxyBypass); + QStringList proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy)); + if (proxyInfo.lpszProxy) + GlobalFree(proxyInfo.lpszProxy); + if (proxyInfo.lpszProxyBypass) + GlobalFree(proxyInfo.lpszProxyBypass); + + if (isBypassed(query.peerHostName(), splitSpaceSemicolon(proxyBypass))) + return sp->defaultResult; + return parseServerList(query, proxyServerList); + } + + // GetProxyForUrl failed + return sp->defaultResult; + } + + // static configuration + if (isBypassed(query.peerHostName(), sp->proxyBypass)) + return sp->defaultResult; + + return parseServerList(query, sp->proxyServerList); +} + +#else // !UNICODE + +QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &) +{ + return QList<QNetworkProxy>() << QNetworkProxy::NoProxy; +} + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/network/kernel/qurlinfo.cpp b/src/network/kernel/qurlinfo.cpp new file mode 100644 index 0000000..255c9ea --- /dev/null +++ b/src/network/kernel/qurlinfo.cpp @@ -0,0 +1,731 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qurlinfo.h" + +#ifndef QT_NO_URLINFO + +#include "qurl.h" +#include "qdir.h" +#include <limits.h> + +QT_BEGIN_NAMESPACE + +class QUrlInfoPrivate +{ +public: + QUrlInfoPrivate() : + permissions(0), + size(0), + isDir(false), + isFile(true), + isSymLink(false), + isWritable(true), + isReadable(true), + isExecutable(false) + {} + + QString name; + int permissions; + QString owner; + QString group; + qint64 size; + + QDateTime lastModified; + QDateTime lastRead; + bool isDir; + bool isFile; + bool isSymLink; + bool isWritable; + bool isReadable; + bool isExecutable; +}; + + +/*! + \class QUrlInfo + \brief The QUrlInfo class stores information about URLs. + + \ingroup io + \ingroup misc + + The information about a URL that can be retrieved includes name(), + permissions(), owner(), group(), size(), lastModified(), + lastRead(), isDir(), isFile(), isSymLink(), isWritable(), + isReadable() and isExecutable(). + + You can create your own QUrlInfo objects passing in all the + relevant information in the constructor, and you can modify a + QUrlInfo; for each getter mentioned above there is an equivalent + setter. Note that setting values does not affect the underlying + resource that the QUrlInfo provides information about; for example + if you call setWritable(true) on a read-only resource the only + thing changed is the QUrlInfo object, not the resource. + + \sa QUrl, {FTP Example} +*/ + +/*! + \enum QUrlInfo::PermissionSpec + + This enum is used by the permissions() function to report the + permissions of a file. + + \value ReadOwner The file is readable by the owner of the file. + \value WriteOwner The file is writable by the owner of the file. + \value ExeOwner The file is executable by the owner of the file. + \value ReadGroup The file is readable by the group. + \value WriteGroup The file is writable by the group. + \value ExeGroup The file is executable by the group. + \value ReadOther The file is readable by anyone. + \value WriteOther The file is writable by anyone. + \value ExeOther The file is executable by anyone. +*/ + +/*! + Constructs an invalid QUrlInfo object with default values. + + \sa isValid() +*/ + +QUrlInfo::QUrlInfo() +{ + d = 0; +} + +/*! + Copy constructor, copies \a ui to this URL info object. +*/ + +QUrlInfo::QUrlInfo(const QUrlInfo &ui) +{ + if (ui.d) { + d = new QUrlInfoPrivate; + *d = *ui.d; + } else { + d = 0; + } +} + +/*! + Constructs a QUrlInfo object by specifying all the URL's + information. + + The information that is passed is the \a name, file \a + permissions, \a owner and \a group and the file's \a size. Also + passed is the \a lastModified date/time and the \a lastRead + date/time. Flags are also passed, specifically, \a isDir, \a + isFile, \a isSymLink, \a isWritable, \a isReadable and \a + isExecutable. +*/ + +QUrlInfo::QUrlInfo(const QString &name, int permissions, const QString &owner, + const QString &group, qint64 size, const QDateTime &lastModified, + const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink, + bool isWritable, bool isReadable, bool isExecutable) +{ + d = new QUrlInfoPrivate; + d->name = name; + d->permissions = permissions; + d->owner = owner; + d->group = group; + d->size = size; + d->lastModified = lastModified; + d->lastRead = lastRead; + d->isDir = isDir; + d->isFile = isFile; + d->isSymLink = isSymLink; + d->isWritable = isWritable; + d->isReadable = isReadable; + d->isExecutable = isExecutable; +} + + +/*! + Constructs a QUrlInfo object by specifying all the URL's + information. + + The information that is passed is the \a url, file \a + permissions, \a owner and \a group and the file's \a size. Also + passed is the \a lastModified date/time and the \a lastRead + date/time. Flags are also passed, specifically, \a isDir, \a + isFile, \a isSymLink, \a isWritable, \a isReadable and \a + isExecutable. +*/ + +QUrlInfo::QUrlInfo(const QUrl &url, int permissions, const QString &owner, + const QString &group, qint64 size, const QDateTime &lastModified, + const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink, + bool isWritable, bool isReadable, bool isExecutable) +{ + d = new QUrlInfoPrivate; + d->name = QFileInfo(url.path()).fileName(); + d->permissions = permissions; + d->owner = owner; + d->group = group; + d->size = size; + d->lastModified = lastModified; + d->lastRead = lastRead; + d->isDir = isDir; + d->isFile = isFile; + d->isSymLink = isSymLink; + d->isWritable = isWritable; + d->isReadable = isReadable; + d->isExecutable = isExecutable; +} + + +/*! + Sets the name of the URL to \a name. The name is the full text, + for example, "http://doc.trolltech.com/qurlinfo.html". + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setName(const QString &name) +{ + if (!d) + d = new QUrlInfoPrivate; + d->name = name; +} + + +/*! + If \a b is true then the URL is set to be a directory; if \a b is + false then the URL is set not to be a directory (which normally + means it is a file). (Note that a URL can refer to both a file and + a directory even though most file systems do not support this.) + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setDir(bool b) +{ + if (!d) + d = new QUrlInfoPrivate; + d->isDir = b; +} + + +/*! + If \a b is true then the URL is set to be a file; if \b is false + then the URL is set not to be a file (which normally means it is a + directory). (Note that a URL can refer to both a file and a + directory even though most file systems do not support this.) + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setFile(bool b) +{ + if (!d) + d = new QUrlInfoPrivate; + d->isFile = b; +} + + +/*! + Specifies that the URL refers to a symbolic link if \a b is true + and that it does not if \a b is false. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setSymLink(bool b) +{ + if (!d) + d = new QUrlInfoPrivate; + d->isSymLink = b; +} + + +/*! + Specifies that the URL is writable if \a b is true and not + writable if \a b is false. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setWritable(bool b) +{ + if (!d) + d = new QUrlInfoPrivate; + d->isWritable = b; +} + + +/*! + Specifies that the URL is readable if \a b is true and not + readable if \a b is false. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setReadable(bool b) +{ + if (!d) + d = new QUrlInfoPrivate; + d->isReadable = b; +} + +/*! + Specifies that the owner of the URL is called \a s. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setOwner(const QString &s) +{ + if (!d) + d = new QUrlInfoPrivate; + d->owner = s; +} + +/*! + Specifies that the owning group of the URL is called \a s. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setGroup(const QString &s) +{ + if (!d) + d = new QUrlInfoPrivate; + d->group = s; +} + +/*! + Specifies the \a size of the URL. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setSize(qint64 size) +{ + if (!d) + d = new QUrlInfoPrivate; + d->size = size; +} + +/*! + Specifies that the URL has access permissions \a p. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setPermissions(int p) +{ + if (!d) + d = new QUrlInfoPrivate; + d->permissions = p; +} + +/*! + Specifies that the object the URL refers to was last modified at + \a dt. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setLastModified(const QDateTime &dt) +{ + if (!d) + d = new QUrlInfoPrivate; + d->lastModified = dt; +} + +/*! + \since 4.4 + + Specifies that the object the URL refers to was last read at + \a dt. + + If you call this function for an invalid URL info, this function + turns it into a valid one. + + \sa isValid() +*/ + +void QUrlInfo::setLastRead(const QDateTime &dt) +{ + if (!d) + d = new QUrlInfoPrivate; + d->lastRead = dt; +} + +/*! + Destroys the URL info object. +*/ + +QUrlInfo::~QUrlInfo() +{ + delete d; +} + +/*! + Assigns the values of \a ui to this QUrlInfo object. +*/ + +QUrlInfo &QUrlInfo::operator=(const QUrlInfo &ui) +{ + if (ui.d) { + if (!d) + d= new QUrlInfoPrivate; + *d = *ui.d; + } else { + delete d; + d = 0; + } + return *this; +} + +/*! + Returns the file name of the URL. + + \sa isValid() +*/ + +QString QUrlInfo::name() const +{ + if (!d) + return QString(); + return d->name; +} + +/*! + Returns the permissions of the URL. You can use the \c PermissionSpec flags + to test for certain permissions. + + \sa isValid() +*/ + +int QUrlInfo::permissions() const +{ + if (!d) + return 0; + return d->permissions; +} + +/*! + Returns the owner of the URL. + + \sa isValid() +*/ + +QString QUrlInfo::owner() const +{ + if (!d) + return QString(); + return d->owner; +} + +/*! + Returns the group of the URL. + + \sa isValid() +*/ + +QString QUrlInfo::group() const +{ + if (!d) + return QString(); + return d->group; +} + +/*! + Returns the size of the URL. + + \sa isValid() +*/ + +qint64 QUrlInfo::size() const +{ + if (!d) + return 0; + return d->size; +} + +/*! + Returns the last modification date of the URL. + + \sa isValid() +*/ + +QDateTime QUrlInfo::lastModified() const +{ + if (!d) + return QDateTime(); + return d->lastModified; +} + +/*! + Returns the date when the URL was last read. + + \sa isValid() +*/ + +QDateTime QUrlInfo::lastRead() const +{ + if (!d) + return QDateTime(); + return d->lastRead; +} + +/*! + Returns true if the URL is a directory; otherwise returns false. + + \sa isValid() +*/ + +bool QUrlInfo::isDir() const +{ + if (!d) + return false; + return d->isDir; +} + +/*! + Returns true if the URL is a file; otherwise returns false. + + \sa isValid() +*/ + +bool QUrlInfo::isFile() const +{ + if (!d) + return false; + return d->isFile; +} + +/*! + Returns true if the URL is a symbolic link; otherwise returns false. + + \sa isValid() +*/ + +bool QUrlInfo::isSymLink() const +{ + if (!d) + return false; + return d->isSymLink; +} + +/*! + Returns true if the URL is writable; otherwise returns false. + + \sa isValid() +*/ + +bool QUrlInfo::isWritable() const +{ + if (!d) + return false; + return d->isWritable; +} + +/*! + Returns true if the URL is readable; otherwise returns false. + + \sa isValid() +*/ + +bool QUrlInfo::isReadable() const +{ + if (!d) + return false; + return d->isReadable; +} + +/*! + Returns true if the URL is executable; otherwise returns false. + + \sa isValid() +*/ + +bool QUrlInfo::isExecutable() const +{ + if (!d) + return false; + return d->isExecutable; +} + +/*! + Returns true if \a i1 is greater than \a i2; otherwise returns + false. The objects are compared by the value, which is specified + by \a sortBy. This must be one of QDir::Name, QDir::Time or + QDir::Size. +*/ + +bool QUrlInfo::greaterThan(const QUrlInfo &i1, const QUrlInfo &i2, + int sortBy) +{ + switch (sortBy) { + case QDir::Name: + return i1.name() > i2.name(); + case QDir::Time: + return i1.lastModified() > i2.lastModified(); + case QDir::Size: + return i1.size() > i2.size(); + default: + return false; + } +} + +/*! + Returns true if \a i1 is less than \a i2; otherwise returns false. + The objects are compared by the value, which is specified by \a + sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size. +*/ + +bool QUrlInfo::lessThan(const QUrlInfo &i1, const QUrlInfo &i2, + int sortBy) +{ + return !greaterThan(i1, i2, sortBy); +} + +/*! + Returns true if \a i1 equals to \a i2; otherwise returns false. + The objects are compared by the value, which is specified by \a + sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size. +*/ + +bool QUrlInfo::equal(const QUrlInfo &i1, const QUrlInfo &i2, + int sortBy) +{ + switch (sortBy) { + case QDir::Name: + return i1.name() == i2.name(); + case QDir::Time: + return i1.lastModified() == i2.lastModified(); + case QDir::Size: + return i1.size() == i2.size(); + default: + return false; + } +} + +/*! + Returns true if this QUrlInfo is equal to \a other; otherwise + returns false. + + \sa lessThan(), equal() +*/ + +bool QUrlInfo::operator==(const QUrlInfo &other) const +{ + if (!d) + return other.d == 0; + if (!other.d) + return false; + + return (d->name == other.d->name && + d->permissions == other.d->permissions && + d->owner == other.d->owner && + d->group == other.d->group && + d->size == other.d->size && + d->lastModified == other.d->lastModified && + d->lastRead == other.d->lastRead && + d->isDir == other.d->isDir && + d->isFile == other.d->isFile && + d->isSymLink == other.d->isSymLink && + d->isWritable == other.d->isWritable && + d->isReadable == other.d->isReadable && + d->isExecutable == other.d->isExecutable); +} + +/*! + \fn bool QUrlInfo::operator!=(const QUrlInfo &other) const + \since 4.2 + + Returns true if this QUrlInfo is not equal to \a other; otherwise + returns false. + + \sa lessThan(), equal() +*/ + +/*! + Returns true if the URL info is valid; otherwise returns false. + Valid means that the QUrlInfo contains real information. + + You should always check if the URL info is valid before relying on + the values. +*/ +bool QUrlInfo::isValid() const +{ + return d != 0; +} + +#endif // QT_NO_URLINFO + +QT_END_NAMESPACE diff --git a/src/network/kernel/qurlinfo.h b/src/network/kernel/qurlinfo.h new file mode 100644 index 0000000..502fd34 --- /dev/null +++ b/src/network/kernel/qurlinfo.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QURLINFO_H +#define QURLINFO_H + +#include <QtCore/qdatetime.h> +#include <QtCore/qstring.h> +#include <QtCore/qiodevice.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +#ifndef QT_NO_URLINFO + +class QUrl; +class QUrlInfoPrivate; + +class Q_NETWORK_EXPORT QUrlInfo +{ +public: + enum PermissionSpec { + ReadOwner = 00400, WriteOwner = 00200, ExeOwner = 00100, + ReadGroup = 00040, WriteGroup = 00020, ExeGroup = 00010, + ReadOther = 00004, WriteOther = 00002, ExeOther = 00001 }; + + QUrlInfo(); + QUrlInfo(const QUrlInfo &ui); + QUrlInfo(const QString &name, int permissions, const QString &owner, + const QString &group, qint64 size, const QDateTime &lastModified, + const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink, + bool isWritable, bool isReadable, bool isExecutable); + QUrlInfo(const QUrl &url, int permissions, const QString &owner, + const QString &group, qint64 size, const QDateTime &lastModified, + const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink, + bool isWritable, bool isReadable, bool isExecutable); + QUrlInfo &operator=(const QUrlInfo &ui); + virtual ~QUrlInfo(); + + virtual void setName(const QString &name); + virtual void setDir(bool b); + virtual void setFile(bool b); + virtual void setSymLink(bool b); + virtual void setOwner(const QString &s); + virtual void setGroup(const QString &s); + virtual void setSize(qint64 size); + virtual void setWritable(bool b); + virtual void setReadable(bool b); + virtual void setPermissions(int p); + virtual void setLastModified(const QDateTime &dt); + void setLastRead(const QDateTime &dt); + + bool isValid() const; + + QString name() const; + int permissions() const; + QString owner() const; + QString group() const; + qint64 size() const; + QDateTime lastModified() const; + QDateTime lastRead() const; + bool isDir() const; + bool isFile() const; + bool isSymLink() const; + bool isWritable() const; + bool isReadable() const; + bool isExecutable() const; + + static bool greaterThan(const QUrlInfo &i1, const QUrlInfo &i2, + int sortBy); + static bool lessThan(const QUrlInfo &i1, const QUrlInfo &i2, + int sortBy); + static bool equal(const QUrlInfo &i1, const QUrlInfo &i2, + int sortBy); + + bool operator==(const QUrlInfo &i) const; + inline bool operator!=(const QUrlInfo &i) const + { return !operator==(i); } + +private: + QUrlInfoPrivate *d; +}; + +#endif // QT_NO_URLINFO + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QURLINFO_H |