HandshakeSession.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "HandshakeSession.h"
00025 #include "Handshaker.h"
00026 #include "RequestHeader.h"
00027 #include "ResponseHeader.h"
00028 #include "Imports.cpp"
00029 
00030 using namespace Gnutella::Handshaking;
00031 
00032 namespace Gnutella {
00033 namespace Handshaking {
00034 
00035 // \todo Maybe define these in package Http
00036 static const int        CodeOK                              = 200;
00037 static const int        CodeNotAcceptable                   = 406;
00038 static const int        CodeBusy                            = 503;
00039 
00040 struct StatusLine
00041 {
00042     int     code;
00043     QString phrase;
00044 };
00045 
00046 static const StatusLine statusLines[] = {
00047 { CodeOK,               "OK" }, // NotRejecting
00048 { CodeBusy,             "No Slots Available" }, // RejectNoSlots
00049 { CodeNotAcceptable,    "You Don't Support Required Features" }, //RejectUnsupportedFeatures,
00050 { CodeNotAcceptable,    "I'm an Ultrapeer" }, //RejectImUltrapeer,
00051 { CodeNotAcceptable,    "I'm a Shielded Leaf" } //RejectImLeaf,
00052 }; // statusLines[]
00053 
00054 enum Constants
00055 {
00056     HandshakingTimeout  = 10000,
00057     TryUltrapeersCount  = 20
00058 };
00059 
00060 } // Handshaking;
00061 } // Gnutella;
00062 
00063 HandshakeSession::HandshakeSession (Connection *connection,
00064                                     const NodeAddress & othersAddress,
00065                                     Handshaker *handshaker,
00066                                     NodeCache *nodeCache)
00067  :  d()
00068 {
00069     d.handshaker            = handshaker;
00070     d.nodeCache             = nodeCache;
00071     d.reader                = new HeaderReader (connection);
00072     d.writer                = new HeaderWriter (connection);
00073     d.connection            = connection;
00074     d.state                 = UnknownState;
00075     d.error                 = NoError;
00076     d.otherNodeInfo.address = othersAddress;
00077 
00078     // If we will try to establish the connection, then updateCache:
00079     if (othersAddress != NodeAddress::Null)
00080         d.nodeCache->updateNode (othersAddress, NodeCache::CheckingAvailability);
00081 
00082     connect (d.connection,  SIGNAL  (connectionEstablished()),
00083              this,          SLOT    (connectionEstablished()));
00084     connect (d.connection,  SIGNAL  (connectionFailed()),
00085              this,          SLOT    (connectionFailed()));
00086     connect (d.connection,  SIGNAL  (connectionClosed()),
00087              this,          SLOT    (connectionClosed()));
00088     connect (d.reader,      SIGNAL  (headerRead()),
00089              this,          SLOT    (handshakeRead()));
00090     connect (d.reader,      SIGNAL  (readError()),
00091              this,          SLOT    (readError()));
00092     connect (d.writer,      SIGNAL  (headerWritten()),
00093              this,          SLOT    (handshakeWritten()));
00094     connect (d.writer,      SIGNAL  (writeError()),
00095              this,          SLOT    (writeError()));
00096 
00097     connect (&d.timer, SIGNAL (timeout()), this, SLOT (timeout()));
00098     d.timer.setSingleShot (true);
00099     d.timer.setInterval (HandshakingTimeout);
00100 }
00101 
00102 HandshakeSession::~HandshakeSession()
00103 {
00104     qDebug() << "HandshakeSession::~HandshakeSession()";
00105 
00106     d.reader->disconnect (this);
00107     d.writer->disconnect (this);
00108     d.timer.disconnect (this);
00109 
00110     d.timer.stop();
00111 
00112     delete d.reader;
00113     delete d.writer;
00114 
00115     qDebug() << "HandshakeSession::~HandshakeSession() exit";
00116 }
00117 
00118 inline void HandshakeSession::completed()
00119 {
00120     d.state = HandshakingCompleted;
00121     d.error = NoError;
00122 
00132     if (d.otherNodeInfo.address != NodeAddress::Null)
00133         d.nodeCache->addNode (d.otherNodeInfo, NodeCache::NodeAvailable);
00134     else if (d.connection->remoteNodeAddress() != NodeAddress::Null)
00135         d.nodeCache->updateNode (d.connection->remoteNodeAddress(),
00136                                  NodeCache::NodeUnavailable);
00137     emit handshakingCompleted (this);
00138 }
00139 
00140 inline void HandshakeSession::failed (HandshakeError error)
00141 {
00142     if (d.state == HandshakingFailed)
00143         return;
00144 
00145     d.state = HandshakingFailed;
00146     d.error = error;
00147 
00148     // \todo NodeBusy or NodeUnavailable depending on error!
00149     if (d.otherNodeInfo.address != NodeAddress::Null)
00150         d.nodeCache->updateNode (d.otherNodeInfo.address,
00151                                  NodeCache::NodeUnavailable);
00152     else if (d.connection->remoteNodeAddress() != NodeAddress::Null)
00153         d.nodeCache->updateNode (d.connection->remoteNodeAddress(),
00154                                  NodeCache::NodeUnavailable);
00155 
00156     // Defer emitting the error signal until the connection is closed (see the
00157     // slot connectionClosed()))
00158 
00159     // Now close only if already connected. If establishing a connection failed
00160     // then connectionFailed() will emit the error signal itself.
00161     if (error != ErrorCannotEstablishConnection)
00162         d.connection->disconnectFromNode();
00163 }
00164 
00165 void HandshakeSession::request(const NodeInfo &myNodeInfo)
00166 {
00167     d.state = SendingPhase1Handshake;
00168 
00169     RequestHeader request (0, 6); // \todo use constants
00170     setHandshakeHeaders (request, myNodeInfo);
00171     qDebug() << "Write:\n" << request.toString(); // Maybe put in writer?
00172     d.timer.start();
00173     d.writer->write (request);
00174 }
00175 
00176 void HandshakeSession::waitRequest()
00177 {
00178     d.state = WaitingPhase1Handshake;
00179     d.timer.start();
00180     d.reader->startReading (new RequestHeader);
00181 }
00182 
00183 void HandshakeSession::sendResponse (RejectReason reason, const NodeInfo &myNodeInfo)
00184 {
00185     int     code    = statusLines [reason].code;
00186     QString phrase  = statusLines [reason].phrase;
00187     ResponseHeader response (code, phrase, 0, 6); // \todo version as consts
00188     setHandshakeHeaders (response, myNodeInfo);
00189     qDebug() << "Write:\n" << response.toString();
00190     d.writer->write (response);
00191 }
00192 
00193 void HandshakeSession::respond (const NodeInfo &myNodeInfo)
00194 {
00195     d.state = SendingPhase2Handshake;
00196     sendResponse (NotRejecting, myNodeInfo);
00197 }
00198 
00199 void HandshakeSession::waitResponse()
00200 {
00201     d.state = WaitingPhase2Handshake;
00202     d.reader->startReading (new ResponseHeader);
00203 }
00204 
00205 void HandshakeSession::waitAcknowledgement()
00206 {
00207     d.state = WaitingPhase3Handshake;
00208     d.reader->startReading (new ResponseHeader);
00209 }
00210 
00211 void HandshakeSession::acknowledge (const NodeInfo &nodeInfo)
00212 {
00213     d.state = SendingPhase3Handshake;
00214     sendResponse (NotRejecting, nodeInfo);
00215 }
00216 
00217 void HandshakeSession::reject (RejectReason reason, const NodeInfo &myNodeInfo)
00218 {
00219     d.state = RejectingConnection;
00220     sendResponse (reason, myNodeInfo);
00221 }
00222 
00223 void HandshakeSession::handshakeRead()
00224 {
00225     const Header *handshake = d.reader->header();
00226     qDebug() << "Read:\n" << handshake->toString();
00227     getHandshakeHeaders (*handshake);
00228 
00229     const RequestHeader *request =
00230             dynamic_cast <const RequestHeader *> (handshake);
00231     const ResponseHeader *response =
00232             dynamic_cast <const ResponseHeader *> (handshake);
00233 
00234     // Badly formatted headers received, abort handshaking.
00235     if ((request == 0 && d.state == WaitingPhase1Handshake)
00236         || (response == 0 && d.state != WaitingPhase1Handshake)) {
00237         failed (ErrorInvalidHeadersRead);
00238     } else if (response && response->statusCode() != CodeOK) {
00239         failed (ErrorRejectedByRemoteNode);
00240     } else if (d.state == WaitingPhase1Handshake) {
00241         emit receivedRequest (this);
00242     } else if (d.state == WaitingPhase2Handshake) {
00243         emit receivedResponse (this);
00244     } else if (d.state == WaitingPhase3Handshake) {
00245         completed();
00246     } else {
00247         Q_ASSERT (false);
00248     }
00249 }
00250 
00251 void HandshakeSession::readError()
00252 {
00253     qDebug() << "HandshakeSession::readError()";
00254     failed (ErrorReadDevice);
00255 }
00256 
00257 void HandshakeSession::handshakeWritten()
00258 {
00259     switch (d.state)
00260     {
00261     case SendingPhase1Handshake:
00262         waitResponse();
00263         break;
00264     case SendingPhase2Handshake:
00265         waitAcknowledgement();
00266         break;
00267     case SendingPhase3Handshake:
00268         completed();
00269         break;
00270     case RejectingConnection:
00271         failed (ErrorRemoteNodeRejected);
00272         break;
00273     case UnknownState:
00274     case WaitingPhase1Handshake:
00275     case WaitingPhase2Handshake:
00276     case WaitingPhase3Handshake:
00277     case HandshakingTimedOut:
00278     case HandshakingFailed:
00279     case HandshakingCompleted:
00280     default:
00281         Q_ASSERT (false);
00282         break;
00283     }
00284 }
00285 
00286 void HandshakeSession::writeError()
00287 {
00288     qDebug() << "HandshakeSession::writeError()";
00289     failed (ErrorWriteDevice);
00290 }
00291 
00292 void HandshakeSession::connectionEstablished()
00293 {
00294     qDebug() << "HandshakeSession::connectionEstablished()";
00295     emit connectionEstablished (this);
00296 }
00297 
00298 void HandshakeSession::connectionFailed()
00299 {
00300     qDebug() << "HandshakeSession::connectionFailed()";
00301     failed (ErrorCannotEstablishConnection);
00302     emit handshakingFailed (this);
00303 }
00304 
00305 void HandshakeSession::connectionClosed()
00306 {
00307     qDebug() << "HandshakeSession::connectionClosed()";
00308     d.timer.stop();
00309     d.timer.disconnect (this);
00310     // If we have not been in failed() already (in other words did not defer
00311     // emitting the error signal), then do the fail case work:
00312     if (d.state != HandshakingFailed)
00313         failed (ErrorRemoteClosedConnection);
00314     // We defered emitting the signal until the connection was finally closed.
00315     // Now we emit only if we did not already think handshaking succeeded (that
00316     // could happen if events get queued in a special order).
00317     if (d.state != HandshakingCompleted)
00318         emit handshakingFailed (this);
00319 }
00320 
00321 void HandshakeSession::timeout()
00322 {
00323     qDebug() << "HandshakeSession::timeout()";
00324     failed (ErrorTimeout);
00325 }
00326 
00327 void HandshakeSession::setHandshakeHeaders (Header &header, const NodeInfo &myNodeInfo)
00328 {
00329     // User-Agent
00330     if (d.myNodeInfo.identification != myNodeInfo.identification)
00331         header.setFieldValue ("User-Agent", myNodeInfo.identification);
00332     if (d.state == SendingPhase1Handshake
00333         || d.state == SendingPhase2Handshake) {
00334         // Listen-IP
00335         header.setFieldValue ("Listen-IP", myNodeInfo.address.toString());
00336 
00337         // Remote-IP (only on request or response)
00338         d.otherNodeInfo.address = d.connection->remoteNodeAddress();
00339         QString remoteIp = d.otherNodeInfo.address.hostAddress().toString();
00340         header.setFieldValue ("Remote-IP", remoteIp);
00341     }
00342     // X-Ultrapeer
00343     {
00344         bool isUltrapeer = myNodeInfo.type != TypeLeaf;
00345         header.setFieldValue ("X-Ultrapeer", isUltrapeer ? "true" : "false");
00346     }
00347     // X-Ultrapeer-Needed - may have changed in last phase, if we just allocated
00348     // the last slot for this peer, so do not print it.
00349     if (d.myNodeInfo.ultrapeerNeeded != myNodeInfo.ultrapeerNeeded
00350         && d.state != SendingPhase3Handshake) {
00351         QString neededString = myNodeInfo.ultrapeerNeeded ? "true" : "false";
00352         header.setFieldValue ("X-Ultrapeer-Needed", neededString);
00353     }
00354 
00355     if (d.state == SendingPhase1Handshake
00356         || d.state == SendingPhase2Handshake) {
00357         header.setFieldValue ("Pong-Caching",
00358                               myNodeInfo.pongCaching.toString());
00359         header.setFieldValue ("GGEP",
00360                               myNodeInfo.ggep.toString());
00361         header.setFieldValue ("X-Query-Routing",
00362                               myNodeInfo.queryRouting.toString());
00363         header.setFieldValue ("X-Ultrapeer-Query-Routing",
00364                               myNodeInfo.ultrapeerQueryRouting.toString());
00365         header.setFieldValue ("X-Dynamic-Querying",
00366                               myNodeInfo.dynamicQuerying.toString());
00367         header.setFieldValue ("X-Degree",
00368                               QString ("%1").arg (myNodeInfo.degree));
00369         header.setFieldValue ("X-Max-TTL",
00370                               QString ("%1").arg (myNodeInfo.maxTtl));
00371         header.setFieldValue ("X-Requeries", "false");
00372         //haeder.setFieldValue ("X-Version", "?.?");
00373         header.setFieldValue ("Vendor-Message",
00374                               myNodeInfo.vendorMessage.toString());
00375         //header.setFieldValue ("X-Ext-Probes", "0.1");
00376         header.setFieldValue ("Bye-Packet",
00377                               myNodeInfo.byePacket.toString());
00378         //header.setFieldValue ("Accept-Encoding", "deflate");
00379     }
00380     // Only write X-Try-Ultrapeers on phase 2, i,e when accepting a connection:
00381     if (d.state == SendingPhase2Handshake) {
00383         NodeSet addresses = d.nodeCache->getNodes (TryUltrapeersCount,
00384                                                    NodeCache::NodeAvailable,
00385                                                    true);
00386         foreach (NodeAddress address, addresses) {
00387             header.addFieldValue ("X-Try-Ultrapeers", address.toString());
00388         }
00389     }
00390     d.myNodeInfo = myNodeInfo;
00391 }
00392 
00393 void HandshakeSession::getHandshakeHeaders (const Header &header)
00394 {
00395     if (header.hasField ("User-Agent")) {
00396         d.otherNodeInfo.identification = header.fieldValue ("User-Agent");
00397     }
00398     if (header.hasField ("Listen-IP")) {
00399         QString listenIp = header.fieldValue ("Listen-IP");
00400         d.otherNodeInfo.address = NodeAddress (listenIp);
00401     }
00402     if (header.hasField ("Remote-IP")) {
00403         QHostAddress remoteIp = QHostAddress (header.fieldValue ("Remote-IP"));
00404         d.handshaker->setVisibleIp (remoteIp);
00405         d.myNodeInfo.address.setHostAddress (remoteIp);
00406     }
00407     if (header.hasField ("X-Ultrapeer")) {
00408         bool isUltrapeer = header.fieldValue ("X-Ultrapeer").toLower() == "true";
00409         d.otherNodeInfo.type = isUltrapeer ? TypePeer : TypeLeaf;
00410         if (isUltrapeer && d.myNodeInfo.type == TypeLeaf)
00411             d.otherNodeInfo.type = TypeUltrapeer;
00412     }
00413     if (header.hasField ("X-Ultrapeer-Needed")) {
00414         d.otherNodeInfo.ultrapeerNeeded = header.fieldValue ("X-Ultrapeer-Needed").toLower() == "true";
00415     } else {
00416         d.otherNodeInfo.ultrapeerNeeded = true; // true if field is missing.
00417     }
00418     if (header.hasField ("X-Try") || header.hasField ("X-Try-Ultrapeers")) {
00419         QStringList values = header.fieldValues("X-Try");
00420         values += header.fieldValues("X-Try-Ultrapeers");
00421         for (int i = values.size() - 1; i >= 0; i--) {
00422             using Gnutella::Bootstrapping::NodeCache;
00423             NodeAddress nodeAddress (values.at (i));
00424             NodeInfo nodeInfo;
00425             nodeInfo.address = nodeAddress;
00426             d.nodeCache->addNode (nodeInfo, NodeCache::UnknownAvailability);
00427         }
00428     }
00429     if (header.hasField ("GGEP")) {
00430         QString version = header.fieldValue ("GGEP");
00431         d.otherNodeInfo.ggep = Version (version);
00432     }
00433     if (header.hasField ("X-Query-Routing")) {
00434         QString version = header.fieldValue ("X-Query-Routing");
00435         d.otherNodeInfo.queryRouting = Version (version);
00436     }
00437     if (header.hasField ("X-Ultrapeer-Query-Routing")) {
00438         QString version = header.fieldValue ("X-Ultrapeer-Query-Routing");
00439         d.otherNodeInfo.ultrapeerQueryRouting = Version (version);
00440     }
00441     if (header.hasField ("Bye-Packet")) {
00442         QString version = header.fieldValue ("Bye-Packet");
00443         d.otherNodeInfo.byePacket = Version (version);
00444     }
00445 }