TorSslSocket.cpp

Go to the documentation of this file.
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 

Generated on Mon Aug 30 19:14:02 2010 for Vidalia by  doxygen 1.5.9