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

Generated on Wed Nov 26 21:04:20 2008 for Vidalia by  doxygen 1.5.7.1