📄 qsslsocket.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtNetwork module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.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 GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************//******************************************************************************** In addition, as a special exception, Trolltech gives permission to link** the code of its release of Qt with the OpenSSL project's "OpenSSL" library** (or modified versions of the "OpenSSL" library that use the same license** as the original version), and distribute the linked executables.**** You must comply with the GNU General Public License version 2 in all** respects for all of the code used other than the "OpenSSL" code. If you** modify this file, you may extend this exception to your version of the file,** but you are not obligated to do so. If you do not wish to do so, delete** this exception statement from your version of this file.******************************************************************************///#define QSSLSOCKET_DEBUG/*! \class QSslSocket \brief The QSslSocket class provides an SSL encrypted socket for both clients and servers. \since 4.3 \reentrant \ingroup io \module network QSslSocket establishes a secure, encrypted TCP connection you can use for transmitting encrypted data. It can operate in both client and server mode, and it supports modern SSL protocols, including SSLv3 and TLSv1. By default, QSslSocket uses SSLv3, but you can change the SSL protocol by calling setProtocol() as long as you do it before the handshake has started. SSL encryption operates on top of the existing TCP stream after the socket enters the ConnectedState. There are two simple ways to establish a secure connection using QSslSocket: With an immediate SSL handshake, or with a delayed SSL handshake occurring after the connection has been established in unencrypted mode. The most common way to use QSslSocket is to construct an object and start a secure connection by calling connectToHostEncrypted(). This method starts an immediate SSL handshake once the connection has been established. \code QSslSocket *socket = new QSslSocket(this); connect(socket, SIGNAL(encrypted()), this, SLOT(ready())); socket->connectToHostEncrypted("imap.example.com", 993); \endcode As with a plain QTcpSocket, QSslSocket enters the HostLookupState, ConnectingState, and finally the ConnectedState, if the connection is successful. The hand shake then starts automatically, and if it succeeds, the encrypted() signal is emitted to indicate the socket has entered the encrypted state and is ready for use. Note that data can be written to the socket immediately after the return from connectToHostEncrypted() (i.e., before the encrypted() signal is emitted). The data is queued in QSslSocket until after the encrypted() signal is emitted. An example of using the delayed SSL handshake to secure an existing connection is the case where an SSL server secures an incoming connection. Suppose you create an SSL server class as a subclass of QTcpServer. You would override QTcpServer::incomingConnection() with something like the example below, which first constructs an instance of QSslSocket and then calls setSocketDescriptor() to set the new socket's descriptor to the exiasting one passed in. It then initiates the SSL handshake by calling startServerEncryption(). \code void SslServer::incomingConnection(int socketDescriptor) { QSslSocket *serverSocket = new QSslSocket; if (serverSocket->setSocketDescriptor(socketDescriptor)) { connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready())); serverSocket->startServerEncryption(); } else { delete serverSocket; } } \endcode If an error occurs, QSslSocket emits signal sslErrors. In this case, if no action is taken to ignore the error(s), the connection is dropped. To continue, despite the occurrence of an error, you can call ignoreSslErrors(), either from within this slot after the error occurs, or anytime after construction of the QSslSocket and before the connection is attempted. This will allow QSslSocket to ignore the errors it encounters when establishing the identity of the peer. Ignoring errors during an SSL handshake should be used with caution, since a fundamental characteristic of secure connections is that they should be established with a successful handshake. Once encrypted, you use QSslSocket as a regular QTcpSocket. When readyRead() is emitted, you can call read(), canReadLine() and readLine(), or getChar() to read decrypted data from QSslSocket's internal buffer, and you can call write() or putChar() to write data back to the peer. QSslSocket will automatically encrypt the written data for you, and emit bytesWritten() once the data has been written to the peer. As a convenience, QSslSocket supports QTcpSocket's blocking functions waitForConnected(), waitForReadyRead(), waitForBytesWritten(), and waitForDisconnected(). It also provides waitForEncrypted(), which will block the calling thread until an encrypted connection has been established. \code QSslSocket socket; socket.connectToHostEncrypted("http.example.com", 443); if (!socket.waitForEncrypted()) { qDebug() << socket.errorString(); return false; } socket.write("GET / HTTP/1.0\r\n\r\n"); while (socket.waitForReadyRead()) qDebug() << socket.readAll().data(); \endcode QSslSocket provides an extensive, easy-to-use API for handling cryptographic ciphers, private keys, and local, peer, and Certification Authority (CA) certificates. It also provides an API for handling errors that occur during the handshake phase. Customize the socket's cryptographic cipher suite before the handshake phase with setCiphers() and setDefaultCiphers(). Customize the socket's local certificate and private key before the handshake phase with setLocalCertificate() and setPrivateKey. Customize the CA certificate database with addCaCertificate(), addCaCertificates(), setCaCertificates(), addDefaultCaCertificate(), addDefaultCaCertificates(), and setDefaultCaCertificates(). For more information about ciphers and certificates, refer to QSslCipher and QSslCertificate. This product uses software developed by the OpenSSL Project for use in the OpenSSL Toolkit. You can download the necessary software from http://www.openssl.org/. \sa QSslCertificate, QSslCipher, QSslError*//*! \enum QSslSocket::SslMode Describes the connection modes available for QSslSocket. \value UnencryptedMode The socket is unencrypted. Its behavior is identical to QTcpSocket. \value SslClientMode The socket is a client-side SSL socket. It is either alreayd encrypted, or it is in the SSL handshake phase (see QSslSocket::isEncrypted()). \value SslServerMode The socket is a client-side SSL socket. It is either already encrypted, or it is in the SSL handshake phase (see QSslSocket::isEncrypted()).*//*! \fn QSslSocket::encrypted() This signal is emitted when QSslSocket enters encrypted mode. After this signal has been emitted, QSslSocket::isEncrypted() will return true, and all further transmissions on the socket will be encrypted. \sa QSslSocket::connectToHostEncrypted(), QSslSocket::isEncrypted()*//*! \fn QSslSocket::modeChanged(QSslSocket::SslMode mode) This signal is emitted when QSslSocket changes from \l QSslSocket::UnencryptedMode to either \l QSslSocket::SslClientMode or \l QSslSocket::SslServerMode. \a mode is the new mode. \sa QSslSocket::mode()*//*! \fn void QSslSocket::sslErrors(const QList<QSslError> &errors); QSslSocket emits this signal during the SSL handshake to indicate that an error has occurred while establishing the identity of the peer. The error is usually an indication that QSslSocket is unable to securely identify the peer. Unless any action is taken, the connection will be dropped after this signal has been emitted. If you want to continue connecting despite the errors that have occurred, you must call QSslSocket::ignoreErrors() from inside a slot connected to this signal. If you need to access the error list at a later point, you can call sslErrors() (without arguments). \a errors contains one or more errors that prevent QSslSocket from verifying the identity of the peer. Note: You cannot use Qt::QueuedConnection when connecting to this signal, or calling QSslSocket::ignoreErrors() will have no effect.*/#include "qsslcipher.h"#include "qsslsocket.h"#include "qsslsocket_openssl_p.h"#include <QtCore/qdebug.h>#include <QtCore/qdir.h>#include <QtCore/qdatetime.h>#include <QtCore/qmutex.h>#include <QtNetwork/qhostaddress.h>#include <QtNetwork/qhostinfo.h>class QSslSocketGlobalData{public: QMutex mutex; QList<QSslCipher> ciphers; QList<QSslCipher> supportedCiphers; QList<QSslCertificate> caCertificates;};Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData)/*! Constructs a QSslSocket object. \a parent is passed to QObject's constructor. The new socket's \l {QSslCipher} {cipher} suite is set to the one returned by the static method defaultCiphers().*/QSslSocket::QSslSocket(QObject *parent) : QTcpSocket(*new QSslSocketBackendPrivate, parent){ Q_D(QSslSocket);#ifdef QSSLSOCKET_DEBUG qDebug() << "QSslSocket::QSslSocket(" << parent << "), this =" << (void *)this;#endif d->q_ptr = this; d->init(); setCiphers(defaultCiphers());}/*! Destroys the QSslSocket.*/QSslSocket::~QSslSocket(){ Q_D(QSslSocket);#ifdef QSSLSOCKET_DEBUG qDebug() << "QSslSocket::~QSslSocket(), this =" << (void *)this;#endif delete d->plainSocket; d->plainSocket = 0;}/*! Starts an encrypted connection to the device \a hostName on \a port, using \a mode as the \l OpenMode. This is equivalent to calling connectToHost() to establish the connection, followed by a call to startClientEncryption(). QSslSocket first enters the HostLookupState. Then, after entering either the event loop or one of the waitFor...() functions, it enters the ConnectingState, emits connected(), and then initiates the SSL client handshake. At each state change, QSslSocket emits signal stateChanged(). After initiating the SSL client handshake, if the identity of the peer can't be established, signal sslErrors() is emitted. If you want to ignore the errors and continue connecting, you must call ignoreSslErrors(), either from inside a slot function connected to the sslErrors() signal, or prior to entering encrypted mode. If ignoreSslErrors is not called, the connection is dropped, signal disconnected() is emitted, and QSslSocket returns to the UnconnectedState. If the SSL handshake is successful, QSslSocket emits encrypted(). \code QSslSocket socket; connect(&socket, SIGNAL(encrypted()), receiver, SLOT(socketEncrypted())); socket.connectToHostEncrypted("imap", 993); socket->write("1 CAPABILITY\r\n"); \endcode \bold{Note:} The example above shows that text can be written to the socket immediately after requesting the encrypted connection, before the encrypted() signal has been emitted. In such cases, the text is queued in the object and written to the socket \e after the connection is established and the encrypted() signal has been emitted. The default for \a mode is \l ReadWrite. If you want to create a QSslSocket on the server side of a connection, you should instead call startServerEncryption() upon receiving the incoming connection through QTcpServer. \sa connectToHost(), startClientEncryption(), waitForConnected(), waitForEncrypted()*/void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode){ Q_D(QSslSocket); if (d->state == ConnectedState || d->state == ConnectingState) { qWarning("QSslSocket::connectToHostEncrypted() called when already connecting/connected"); return; } connectToHost(hostName, port, mode); d->autoStartHandshake = true;}/*! Initializes QSslSocket with the native socket descriptor \a socketDescriptor. Returns true if \a socketDescriptor is accepted as a valid socket descriptor; otherwise returns false. The socket is opened in the mode specified by \a openMode, and enters the socket state specified by \a state. \bold{Note:} It is not possible to initialize two sockets with the same native socket descriptor. \sa socketDescriptor()*/bool QSslSocket::setSocketDescriptor(int socketDescriptor, SocketState state, OpenMode openMode){ Q_D(QSslSocket);#ifdef QSSLSOCKET_DEBUG qDebug() << "QSslSocket::setSocketDescriptor(" << socketDescriptor << "," << state << "," << openMode << ")";#endif if (!d->plainSocket) d->createPlainSocket(openMode); bool retVal = d->plainSocket->setSocketDescriptor(socketDescriptor, state, openMode); d->cachedSocketDescriptor = d->plainSocket->socketDescriptor(); setSocketError(d->plainSocket->error()); setSocketState(state); setOpenMode(openMode); setLocalPort(d->plainSocket->localPort()); setLocalAddress(d->plainSocket->localAddress()); setPeerPort(d->plainSocket->peerPort()); setPeerAddress(d->plainSocket->peerAddress()); setPeerName(d->plainSocket->peerName()); return retVal;}/*! Returns the current mode for the socket; either UnencryptedMode, where QSslSocket behaves identially to QTcpSocket, or one of SslClientMode or SslServerMode, where the client is either negotiating or in encrypted mode. When the mode changes, QSslSocket emits modeChanged() \sa SslMode*/QSslSocket::SslMode QSslSocket::mode() const{ Q_D(const QSslSocket); return d->mode;}/*! Returns true if the socket is encrypted; otherwise, false is returned. An encrypted socket encrypts all data that is written by calling write() or putChar() before the data is written to the network, and descrypts all incoming data as the data is received from the network, before you call read(), readLine() or getChar(). QSslSocket emits encrypted() when it enters encrypted mode. You can call sessionCipher() to find which cryptographic cipher is used to encrypt and decrypt your data. \sa mode()*/bool QSslSocket::isEncrypted() const{ Q_D(const QSslSocket); return d->connectionEncrypted;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -