Packet.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 
00033 #include "Qt.h"
00034 #include "Packet.h"
00035 #include "Bye.h"
00036 #include "Ping.h"
00037 #include "Pong.h"
00038 #include "Push.h"
00039 #include "Query.h"
00040 #include "QueryHits.h"
00041 #include "QueryRouting.h"
00042 #include "QueryRoutingPatch.h"
00043 #include "QueryRoutingReset.h"
00044 #include "UnknownPacket.h"
00045 #include "VendorMessages/VendorMessage.h"
00046 
00047 using namespace Gnutella::Packets;
00048 
00050 
00053 QDataStream & Gnutella::Packets::operator>> (QDataStream &in, PayloadDescriptor &pd)
00054 {
00055     uchar tmp;
00056     in >> tmp;
00057     pd = static_cast<PayloadDescriptor> (tmp);
00058     return in;
00059 }
00060 
00062 
00065 QDataStream & Gnutella::Packets::operator<< (QDataStream &out, const PayloadDescriptor &pd)
00066 {
00067     return out << static_cast<uchar> (pd);
00068 }
00069 
00071 
00075 QDataStream & Gnutella::Packets::operator>> (QDataStream &in, QHostAddress &ha)
00076 {
00077     QDataStream::ByteOrder oldByteOrder = in.byteOrder();
00078     in.setByteOrder (QDataStream::BigEndian);
00079     quint32 ipAddress;
00080     in >> ipAddress;
00081     ha.setAddress (ipAddress);
00082     in.setByteOrder (oldByteOrder);
00083     return in;
00084 }
00085 
00087 
00091 QDataStream & Gnutella::Packets::operator<< (QDataStream &out, const QHostAddress &ha)
00092 {
00093     QDataStream::ByteOrder oldByteOrder = out.byteOrder();
00094     out.setByteOrder (QDataStream::BigEndian);
00095     out << ha.toIPv4Address();
00096     out.setByteOrder (oldByteOrder);
00097     return out;
00098 }
00099 
00101 
00109 QDataStream & Gnutella::Packets::operator>> (QDataStream &in, Extensions::Extension &extension)
00110 {
00111     extension.read (in);
00112     return in;
00113 }
00114 
00116 
00124 QDataStream & Gnutella::Packets::operator<< (QDataStream &out, const Extensions::Extension &extension)
00125 {
00126     extension.write (out);
00127     return out;
00128 }
00129 
00141 Packet * Packet::fromRawData (const QByteArray &rawHeader, const QByteArray &rawPayload)
00142 {
00143     int payloadDescriptor = static_cast <uchar> (rawHeader[16]);
00144 
00145 // \todo Quick hack!
00146 #ifdef NO_PACKET_VIRTUAL_CTOR
00147     return 0;
00148 #endif
00149 
00150     switch (static_cast <uchar> (payloadDescriptor))
00151     {
00152     case PingDescriptor:
00153         return new Ping (rawHeader, rawPayload);
00154     case PongDescriptor:
00155         return new Pong (rawHeader, rawPayload);
00156     case QueryDescriptor:
00157         return new Query (rawHeader, rawPayload);
00158     case QueryHitsDescriptor:
00159         return new QueryHits (rawHeader, rawPayload);
00160     case PushDescriptor:
00161         return new Push (rawHeader, rawPayload);
00162     case QueryRoutingDescriptor:
00163         {
00164             QueryRouting::Variant variant = QueryRouting::UnknownVariant;
00165             if (rawPayload.length() > 0)
00166                 variant = static_cast <QueryRouting::Variant> (rawPayload[0]);
00167 
00168             switch (variant){
00169             case QueryRouting::ResetVariant:
00170                 return new QueryRoutingReset (rawHeader, rawPayload);
00171             case QueryRouting::PatchVariant:
00172                 return new QueryRoutingPatch (rawHeader, rawPayload);
00173             case QueryRouting::UnknownVariant:
00174             default:
00175                 return new UnknownPacket (rawHeader, rawPayload);
00176             }
00177         }
00178     case ByeDescriptor:
00179         return new Bye (rawHeader, rawPayload);
00180     case IbmcDescriptor:
00181     case OpenVendorDescriptor:
00182     case StandardVendorDescriptor:
00183         return VendorMessages::VendorMessage::fromRawData (rawHeader, rawPayload);
00184     default:
00185         return new UnknownPacket (rawHeader, rawPayload);
00186     }
00187 }
00188 
00197 Packet::Packet(PayloadDescriptor payloadDescriptor)
00198 {
00199     d = new Data;
00200 
00201     d->ref.init (1);
00202 
00203     d->descriptorId = QUuid::createUuid();
00204     d->hops = DefaultHops;
00205     d->ttl = DefaultTtl;
00206     d->payloadDescriptor = payloadDescriptor;
00207     d->payloadLength = 0;
00208 
00209     d->rewriteHeader = true;
00210     d->rewritePayload = true;
00211     d->isValid = true;
00212 }
00213 
00226 Packet::Packet (const QByteArray &rawHeader, const QByteArray &rawPayload)
00227 {
00228     d = new Data;
00229 
00230     d->ref.init (1);
00231 
00232     d->rawHeader = rawHeader;
00233     d->rawPayload = rawPayload;
00234     d->rewriteHeader = false;
00235     d->rewritePayload = false;
00236     d->isValid = false;
00237 }
00238 
00240 
00243 Packet::~Packet()
00244 {
00245     if (!d->ref.deref())
00246         delete d;
00247 }
00248 
00250 
00253 Packet & Packet::operator= (const Packet &other)
00254 {
00255     qAtomicAssign (d, other.d);
00256     return *this;
00257 }
00258 
00262 void Packet::invalidateHeader()
00263 {
00264     qAtomicDetach(d);
00265     d->ref.init(1);
00266     d->rewriteHeader = true;
00267 }
00268 
00279 void Packet::invalidatePayload()
00280 {
00281     invalidateHeader(); // First copy private data.
00282     d->rewritePayload = true;
00283 }
00284 
00297 void Packet::parse()
00298 {
00299     Q_ASSERT (d->ref == 1); // parse() is only called on a new object!
00300 
00301     d->isValid = true;
00302     if (prepareReadHeader (d->rawHeader)) {
00303         QDataStream headerStream (d->rawHeader);
00304         headerStream.setByteOrder (QDataStream::LittleEndian);
00305         readHeader (headerStream);
00306     } else
00307         d->isValid = false;
00308 
00309     Q_ASSERT (d->payloadLength == static_cast <uint> (d->rawPayload.length()));
00310 
00311     if (prepareReadPayload (d->rawPayload)) {
00312         QDataStream payloadStream (d->rawPayload);
00313         payloadStream.setByteOrder (QDataStream::LittleEndian);
00314         readPayload (payloadStream);
00315     } else
00316         d->isValid = false;
00317 }
00318 
00322 bool Packet::prepareReadHeader (const QByteArray &rawHeader) const
00323 {
00324     return rawHeader.length() == HeaderLength;
00325 }
00326 
00336 void Packet::readHeader (QDataStream &stream)
00337 {
00338     stream >> d->descriptorId;
00339     stream >> d->payloadDescriptor;
00340     stream >> d->ttl;
00341     stream >> d->hops;
00342     stream >> d->payloadLength;
00343 }
00344 
00354 void Packet::writeHeader (QDataStream &stream) const
00355 {
00356     stream << d->descriptorId;
00357     stream << d->payloadDescriptor;
00358     stream << d->ttl;
00359     stream << d->hops;
00360     stream << d->payloadLength;
00361 }
00362 
00388 QByteArray Packet::rawHeader() const
00389 {
00390     if (!isValid())
00391         return QByteArray();
00392 
00393     if (d->rewriteHeader) {
00394         // Force rebuilding the payload in order to get the new payloadLength.
00395         if (d->rewritePayload)
00396             rawPayload();
00397 
00398         // Prepare write.
00399         d->rawHeader.resize (HeaderLength);
00400 
00401         // Do write.
00402         QDataStream stream (&d->rawHeader, QIODevice::WriteOnly);
00403         stream.setByteOrder (QDataStream::LittleEndian);
00404         writeHeader (stream);
00405 
00406         // See rawPayload() for more details on this:
00407         Q_ASSERT (d->rawHeader.length() == HeaderLength);
00408         d->rawHeader.resize (HeaderLength);
00409 
00410         d->rewriteHeader = false;
00411     }
00412     return d->rawHeader;
00413 }
00414 
00441 QByteArray Packet::rawPayload() const
00442 {
00443     if (!isValid())
00444         return QByteArray();
00445 
00446     if (d->rewritePayload) {
00447         // Prepare write.
00448         d->payloadLength = prepareWritePayload();
00449         d->rawPayload.resize (d->payloadLength);
00450 
00451         // Do write.
00452         QDataStream stream (&d->rawPayload, QIODevice::WriteOnly);
00453         stream.setByteOrder (QDataStream::LittleEndian);
00454         writePayload (stream);
00455 
00456         // It may happen that due to a bug more data is written to the stream
00457         // then the number of bytes declared by prepareWritePayload(). In this
00458         // case d->rawPayload is resized by the stream in order for the data to
00459         // fit. Assert and truncate the buffer.
00460         Q_ASSERT (d->payloadLength ==
00461                   static_cast <uint> (d->rawPayload.length()));
00462         d->rawPayload.resize (d->payloadLength);
00463 
00464         d->rewritePayload = false;
00465     }
00466     return d->rawPayload;
00467 }
00468