Vidalia 0.2.10
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If you 00004 ** did not receive the LICENSE file with this file, you may obtain it from the 00005 ** Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.vidalia-project.net/. No part of Vidalia, including this file, 00007 ** may be copied, modified, propagated, or distributed except according to the 00008 ** terms described in the LICENSE file. 00009 */ 00010 00011 /* 00012 ** \file TorSslSocket.cpp 00013 ** \version $Id: /local/vidalia/trunk/src/util/torsocket.cpp 1564 2006-12-26T06:06:04.965088Z edmanm $ 00014 ** \brief A QSslSocket that makes encrypted requests over Tor 00015 */ 00016 00017 #include "TorSslSocket.h" 00018 00019 #include <QDataStream> 00020 #include <QStringList> 00021 00022 #define SOCKS_VERSION 0x04 /**< SOCKS version. */ 00023 #define SOCKS_CONNECT 0x01 /**< SOCKS connect command ID. */ 00024 #define SOCKS_FAKE_IP 0x00000001 /**< Bogus IP. */ 00025 #define SOCKS_RESPONSE_LEN 0x08 /**< SOCKS server response length. */ 00026 #define SOCKS_RESPONSE_VERSION 0x00 /**< SOCKS server response version. */ 00027 #define SOCKS_CONNECT_STATUS_OK 0x5A /**< SOCKS server response status. */ 00028 00029 00030 /** Constructor. */ 00031 TorSslSocket::TorSslSocket(const QHostAddress &socksAddr, 00032 quint16 socksPort, QObject *parent) 00033 : QSslSocket(parent), 00034 _socksAddr(socksAddr), 00035 _socksPort(socksPort) 00036 { 00037 QObject::connect(this, SIGNAL(sslErrors(QList<QSslError>)), 00038 this, SLOT(onSslErrors(QList<QSslError>))); 00039 QObject::connect(this, SIGNAL(error(QAbstractSocket::SocketError)), 00040 this, SLOT(onError(QAbstractSocket::SocketError))); 00041 QObject::connect(this, SIGNAL(readyRead()), 00042 this, SLOT(onHandshakeResponse())); 00043 QObject::connect(this, SIGNAL(connected()), 00044 this, SLOT(connectedToProxy())); 00045 QObject::connect(this, SIGNAL(encrypted()), 00046 this, SLOT(onEncrypted())); 00047 } 00048 00049 /** Connects to the specified hostname and port via Tor. */ 00050 void 00051 TorSslSocket::connectToRemoteHost(const QString &remoteHost, quint16 remotePort, 00052 bool encrypted) 00053 { 00054 _remoteHost = remoteHost; 00055 _remotePort = remotePort; 00056 _encrypted = encrypted; 00057 QTcpSocket::connectToHost(_socksAddr, _socksPort); 00058 } 00059 00060 /** Called when a connection error has occurred. */ 00061 void 00062 TorSslSocket::onError(QAbstractSocket::SocketError error) 00063 { 00064 Q_UNUSED(error); 00065 emit socketError(errorString()); 00066 } 00067 00068 /** Called when one or more SSL errors occur on the socket. */ 00069 void 00070 TorSslSocket::onSslErrors(const QList<QSslError> &errors) 00071 { 00072 QStringList errorStrings; 00073 foreach (QSslError error, errors) { 00074 errorStrings << "\"" + error.errorString() + "\""; 00075 } 00076 emit socketError(errorStrings.join(",")); 00077 } 00078 00079 /** Called when a connection has been established to the proxy host and starts 00080 * a Socks4a handshake. */ 00081 void 00082 TorSslSocket::connectedToProxy() 00083 { 00084 sendSocksHandshake(_remoteHost, _remotePort); 00085 } 00086 00087 /** Called when an encrypted connection has been established to the remote 00088 * host. */ 00089 void 00090 TorSslSocket::onEncrypted() 00091 { 00092 emit connectedToRemoteHost(); 00093 } 00094 00095 /** Sends the first part of a Socks4a handshake, using the remote hostname and 00096 * port specified in the previous call to connectToHost(). The message should 00097 * be formatted as follows: 00098 * 00099 * 0x04 (socks version) 00100 * 0x01 (connect) 00101 * PORT (two bytes, most significant byte first) 00102 * 0x00 0x00 0x00 0x01 (fake IP address: tells proxy to use SOCKS4a) 00103 * 0x00 (empty username field) 00104 * HOSTNAME (target hostname) 00105 * 0x00 (marks the end of the hostname field) 00106 */ 00107 void 00108 TorSslSocket::sendSocksHandshake(const QString &remoteHost, quint16 remotePort) 00109 { 00110 QDataStream sock(this); 00111 sock << (quint8)SOCKS_VERSION; 00112 sock << (quint8)SOCKS_CONNECT; 00113 sock << (quint16)remotePort; 00114 sock << (quint32)SOCKS_FAKE_IP; 00115 sock << (quint8)0; 00116 sock.writeRawData(qPrintable(remoteHost), remoteHost.length()); 00117 sock << (quint8)0; 00118 } 00119 00120 /** Handles the second half of the handshake, received from the SOCKS 00121 * proxy server. The response should be formatted as follows: 00122 * 00123 * 0x00 (response version) 00124 * STATUS (0x5A means success; other values mean failure) 00125 * PORT (not set) 00126 * ADDRESS (not set) 00127 */ 00128 void 00129 TorSslSocket::onHandshakeResponse() 00130 { 00131 QByteArray response; 00132 if (bytesAvailable() >= SOCKS_RESPONSE_LEN) { 00133 /* We've received our response, so stop waiting for it. */ 00134 QObject::disconnect(this, SIGNAL(readyRead()), 00135 this, SLOT(onHandshakeResponse())); 00136 00137 /* Read the 8-byte response off the socket. */ 00138 response = read(SOCKS_RESPONSE_LEN); 00139 00140 /* Check to make sure we got a good response from the proxy. */ 00141 if ((uchar)response[0] == (uchar)SOCKS_RESPONSE_VERSION && 00142 (uchar)response[1] == (uchar)SOCKS_CONNECT_STATUS_OK) { 00143 if (_encrypted) { 00144 /* Connection status was okay, so start client encryption. */ 00145 /* We first need to set the peer name to the intended remote host, 00146 * otherwise Qt will use the proxy (e.g., 127.0.0.1) as the peer name 00147 * when validating the server certificate. */ 00148 setPeerName(_remoteHost); 00149 startClientEncryption(); 00150 } else { 00151 /* Caller wanted an unencrypted, unauthenticated, uncool conn. */ 00152 emit connectedToRemoteHost(); 00153 } 00154 } else { 00155 /* Remote connection failed, so close the connection to the proxy. */ 00156 disconnectFromHost(); 00157 } 00158 } 00159 } 00160