Handshaker.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 "Handshaker.h"
00025 #include "HandshakeSession.h"
00026 #include "RequestHeader.h"
00027 #include "ResponseHeader.h"
00028 #include "SlotAllocator.h"
00029 #include "Imports.cpp"
00030 
00031 using namespace Gnutella::Handshaking;
00032 
00033 namespace Gnutella {
00034 namespace Handshaking {
00035 
00036 class HandshakerPrivate
00037 {
00038     REFERENCE_OBJECT (HandshakerPrivate)
00039 
00040 public:
00041     typedef QSet <HandshakeSession *>       HandshakeSessions;
00042 
00043     NodeInfo                                nodeInfo; // NodeInfo for our node.
00044     LocalPeer                               *localPeer;
00045     NodeCache                               *nodeCache;
00046     SlotAllocator                           *slotAllocator;
00047     HandshakeSessions                       sessions;
00048 
00049     HandshakerPrivate() : nodeInfo(), localPeer (0), nodeCache (0),
00050                           slotAllocator (0), sessions()
00051     {}
00052 };
00053 
00054 } // namespace Handshaking;
00055 } // namespace Gnutella;
00056 
00057 Handshaker::Handshaker (LocalPeer *localPeer,
00058                         NodeCache *nodeCache,
00059                         SlotAllocator *slotAllocator)
00060  :  p (new HandshakerPrivate)
00061 {
00062     p->localPeer = localPeer;
00063     p->nodeCache = nodeCache;
00064     p->slotAllocator = slotAllocator;
00065 
00066 
00067     // Initialize  our node info:
00068     p->nodeInfo.address.setHostPort (localPeer->serverPort());
00069     p->nodeInfo.type = localPeer->nodeInfo().type;
00070     p->nodeInfo.identification = "Calitko/0.6.1";
00071     //p->nodeInfo.ultrapeerNeeded = slotAllocator->hasFreePeerSlots();
00072     p->nodeInfo.pongCaching = Version (0, 1);
00073     p->nodeInfo.ggep = Version (0, 5);
00074     p->nodeInfo.queryRouting = Version (0, 1);
00075     p->nodeInfo.ultrapeerQueryRouting = Version (0, 1);
00076     p->nodeInfo.dynamicQuerying = Version (0, 1);
00077     //d.nodeInfo.guess = Version (0, 1);
00078     p->nodeInfo.vendorMessage = Version (0, 1);
00079     //d.nodeInfo.extProbes = Version (0, 1);
00080     p->nodeInfo.byePacket = Version (0, 1);
00081     p->nodeInfo.degree = 32;
00082     p->nodeInfo.maxTtl = 3;
00083     //p->encoding = QString ("deflate");
00084 }
00085 
00086 Handshaker::~Handshaker()
00087 {
00088     delete p;
00089 }
00090 
00091 HandshakeSession * Handshaker::newSession (Connection *connection,
00092                                            const NodeAddress &nodeAddress)
00093 {
00094     // \todo That's a workaround to get the value update. Fix it.
00095     p->nodeInfo.address.setHostPort (p->localPeer->serverPort());
00096     p->nodeInfo.ultrapeerNeeded = p->slotAllocator->hasFreePeerSlots();
00097     p->nodeInfo.type = p->localPeer->nodeInfo().type;
00098 
00099     // \todo Perform some check whether connection is given for the second time?
00100     HandshakeSession *session = 0;
00101     session = new HandshakeSession (connection, nodeAddress,
00102                                     this, p->nodeCache);
00103     p->sessions.insert (session);
00104 
00105     connect (session, SIGNAL (connectionEstablished (HandshakeSession *)),
00106              this, SLOT (connectionEstablished (HandshakeSession *)));
00107     connect (session, SIGNAL (receivedRequest (HandshakeSession *)),
00108              this, SLOT (receivedRequest (HandshakeSession *)));
00109     connect (session, SIGNAL (receivedResponse (HandshakeSession *)),
00110              this, SLOT (receivedResponse (HandshakeSession *)));
00111     connect (session, SIGNAL (handshakingCompleted (HandshakeSession *)),
00112              this, SLOT (handshakingCompleted (HandshakeSession *)));
00113     connect (session, SIGNAL (handshakingFailed (HandshakeSession *)),
00114              this, SLOT (handshakingFailed (HandshakeSession *)));
00115 
00116     return session;
00117 }
00118 
00119 void Handshaker::deleteSession (HandshakeSession *session)
00120 {
00121     Q_ASSERT (p->sessions.contains (session));
00122     p->sessions.remove (session);
00123     session->disconnect (this);
00124     session->deleteLater();
00125 }
00126 
00127 void Handshaker::requestConnection (Connection *connection,
00128                                     const NodeAddress & nodeAddress)
00129 {
00130     qDebug() << "Handshaker::requestConnection()";
00131 
00132     HandshakeSession *session = newSession (connection, nodeAddress);
00133     p->slotAllocator->allocateSlot (session);
00134     /*  \todo When HandshakeSession is created it initializes its otherNode.address
00135         member to the remoteAddress of the connection, which in this case here is
00136         not connected! Therefore if the connection attempt fails, the address remains
00137         Null, which causes that a Null address is added to the NodeCache, which is
00138         absolustely wrong! Now a workaround is that addNode returns without adding a
00139         Null address! Better would be to make sure you never pass it a Null address! */
00140     connection->connectToNode (nodeAddress);
00141 
00142     qDebug() << "Handshaker::requestConnection() done";
00143 }
00144 
00145 void Handshaker::acceptConnection (Connection *connection)
00146 {
00147     HandshakeSession *session = newSession (connection, NodeAddress::Null);
00148     p->slotAllocator->allocateSlot (session);
00149     session->waitRequest();
00150 }
00151 
00152 void Handshaker::connectionEstablished (HandshakeSession *session)
00153 {
00154     Connection *connection = session->connection();
00155     emit handshakingStarted (connection);
00156     session->request (p->nodeInfo);
00157 }
00158 
00159 void Handshaker::handshakingCompleted (HandshakeSession *session)
00160 {
00161     Connection *connection = session->connection();
00162     NodeInfo nodeInfo = session->otherNodeInfo();
00163     connection->disconnect (session); // better add a releaseConnection() function to session and do that there.
00164     deleteSession (session); // Disconnect all session's slots here.
00165     emit handshakingCompleted (connection, nodeInfo);
00166 }
00167 
00168 void Handshaker::handshakingFailed (HandshakeSession *session)
00169 {
00170     qDebug() << "Handshaker::failedHandshaking" << session;
00171 
00172     Connection *connection = session->connection();
00173     HandshakeError error = session->error();
00174     NodeInfo nodeInfo = session->otherNodeInfo();
00175     connection->disconnect (session); // better add a releaseConnection() function to session and do that there.
00176     deleteSession (session); // Disconnect all session's slots here.
00177     connection->deleteLater();
00178     // \todo connection is already deleted! Do we need the signal at all? Not now at least! Maybe LocalPeer should delete the connections after all?
00179     emit handshakingFailed (connection, error);
00180 }
00181 
00182 bool Handshaker::hasRequiredFeatures (const NodeInfo &nodeInfo)
00183 {
00184     // Does not support ultrapeers (no X-Ultrapeer header field)
00185     // \todo Handle Crawlers correctly!
00186     if (nodeInfo.type == TypeUnknown)
00187         return false;
00188     if (nodeInfo.type == TypeLeaf
00189         && nodeInfo.queryRouting.minorVersion() < 1)
00190         return false;
00191     if (nodeInfo.type == TypePeer
00192         && nodeInfo.ultrapeerQueryRouting.minorVersion() < 1)
00193         return false;
00194     return true;
00195 }
00196 
00197 bool Handshaker::compatibleNodeTypes (const NodeInfo &otherNodeInfo)
00198 {
00200     if (p->nodeInfo.type == TypeLeaf) {
00201         return otherNodeInfo.type == TypeUltrapeer;
00202     }
00203     if (p->nodeInfo.type == TypeUltrapeer || p->nodeInfo.type == TypePeer) {
00204         if (otherNodeInfo.type == TypePeer)
00205             return otherNodeInfo.ultrapeerNeeded == true;
00206         else
00207             return true;
00208     }
00209     Q_ASSERT (false);
00210     return false;
00211 }
00212 
00213 int Handshaker::getRejectReason (HandshakeSession *session)
00214 {
00215     NodeInfo otherNodeInfo = session->otherNodeInfo();
00216     if (!hasRequiredFeatures (otherNodeInfo))
00217         return RejectUnsupportedFeatures;
00218     if (!compatibleNodeTypes (otherNodeInfo)) {
00219         if (p->nodeInfo.type == TypeLeaf)
00220             return RejectImLeaf;
00221         else
00222             return RejectImUltrapeer;
00223     } else if (!p->slotAllocator->hasSlotFor (otherNodeInfo))
00224         return RejectNoSlots;
00225     else
00226         return NotRejecting;
00227 }
00228 
00229 void Handshaker::receivedRequest (HandshakeSession *session)
00230 {
00231     p->nodeInfo.address.setHostAddress (session->myNodeInfo().address.hostAddress());
00232     RejectReason reason = static_cast <RejectReason> (getRejectReason (session));
00233     if (reason != NotRejecting)
00234         session->reject (reason, p->nodeInfo);
00235     else {
00236         session->respond (p->nodeInfo);
00237     }
00238 }
00239 
00240 void Handshaker::receivedResponse (HandshakeSession *session)
00241 {
00242     p->nodeInfo.address.setHostAddress (session->myNodeInfo().address.hostAddress());
00243     RejectReason reason = static_cast <RejectReason> (getRejectReason (session));
00244     if (reason != NotRejecting)
00245         session->reject (reason, p->nodeInfo);
00246     else {
00247         session->acknowledge (p->nodeInfo);
00248     }
00249 }
00250 
00251 void Handshaker::setVisibleIp (const QHostAddress &address)
00252 {
00253     p->localPeer->setServerIpAddress (address);
00254 }