gloox 1.0

socks5bytestreammanager.cpp

00001 /*
00002   Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
00011 */
00012 
00013 
00014 #include "bytestreamhandler.h"
00015 #include "socks5bytestreammanager.h"
00016 #include "socks5bytestreamserver.h"
00017 #include "socks5bytestream.h"
00018 #include "clientbase.h"
00019 #include "disco.h"
00020 #include "error.h"
00021 #include "connectionbase.h"
00022 #include "sha.h"
00023 #include "util.h"
00024 
00025 #include <cstdlib>
00026 
00027 namespace gloox
00028 {
00029 
00030   // ---- SOCKS5BytestreamManager::Query ----
00031   static const char* s5bModeValues[] =
00032   {
00033     "tcp", "udp"
00034   };
00035 
00036   static inline const char* modeString( SOCKS5BytestreamManager::S5BMode mode )
00037   {
00038     return s5bModeValues[mode];
00039   }
00040 
00041   SOCKS5BytestreamManager::Query::Query()
00042     : StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid )
00043   {
00044   }
00045 
00046   SOCKS5BytestreamManager::Query::Query( const std::string& sid, S5BMode mode,
00047          const StreamHostList& hosts )
00048     : StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_mode( mode ), m_hosts( hosts ), m_type( TypeSH )
00049   {
00050   }
00051 
00052   SOCKS5BytestreamManager::Query::Query( const JID& jid, const std::string& sid, bool activate )
00053     : StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_jid( jid ), m_type( activate ? TypeA : TypeSHU )
00054   {
00055   }
00056 
00057   SOCKS5BytestreamManager::Query::Query( const Tag* tag )
00058     : StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid )
00059   {
00060     if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_BYTESTREAMS
00061         /*|| !tag->hasAttribute( "sid" )*/ )
00062       return;
00063 
00064     m_sid = tag->findAttribute( "sid" );
00065     m_mode = static_cast<S5BMode>( util::deflookup( tag->findAttribute( "mode" ), s5bModeValues, S5BTCP ) );
00066 
00067     const TagList& l = tag->children();
00068     TagList::const_iterator it = l.begin();
00069     for( ; it != l.end(); ++it )
00070     {
00071       if( (*it)->name() == "streamhost" && (*it)->hasAttribute( "jid" )
00072             && (*it)->hasAttribute( "host" ) && (*it)->hasAttribute( "port" ) )
00073       {
00074         m_type = TypeSH;
00075         StreamHost sh;
00076         sh.jid = (*it)->findAttribute( "jid" );
00077         sh.host = (*it)->findAttribute( "host" );
00078         sh.port = atoi( (*it)->findAttribute( "port" ).c_str() );
00079         m_hosts.push_back( sh );
00080       }
00081       else if( (*it)->name() == "streamhost-used" )
00082       {
00083         m_type = TypeSHU;
00084         m_jid = (*it)->findAttribute( "jid" );
00085       }
00086       else if( (*it)->name() == "activate" )
00087       {
00088         m_type = TypeA;
00089         m_jid = (*it)->cdata();
00090       }
00091     }
00092   }
00093 
00094   SOCKS5BytestreamManager::Query::~Query()
00095   {
00096   }
00097 
00098   const std::string& SOCKS5BytestreamManager::Query::filterString() const
00099   {
00100     static const std::string filter = "/iq/query[@xmlns='" + XMLNS_BYTESTREAMS + "']";
00101     return filter;
00102   }
00103 
00104   Tag* SOCKS5BytestreamManager::Query::tag() const
00105   {
00106     if( m_type == TypeInvalid /*|| m_sid.empty()*/ )
00107       return 0;
00108 
00109     Tag* t = new Tag( "query" );
00110     t->setXmlns( XMLNS_BYTESTREAMS );
00111     t->addAttribute( "sid", m_sid );
00112     switch( m_type )
00113     {
00114       case TypeSH:
00115       {
00116         t->addAttribute( "mode", util::deflookup( m_mode, s5bModeValues, "tcp" ) );
00117         StreamHostList::const_iterator it = m_hosts.begin();
00118         for( ; it != m_hosts.end(); ++it )
00119         {
00120           Tag* s = new Tag( t, "streamhost" );
00121           s->addAttribute( "jid", (*it).jid.full() );
00122           s->addAttribute( "host", (*it).host );
00123           s->addAttribute( "port", (*it).port );
00124         }
00125         break;
00126       }
00127       case TypeSHU:
00128       {
00129         Tag* s = new Tag( t, "streamhost-used" );
00130         s->addAttribute( "jid", m_jid.full() );
00131         break;
00132       }
00133       case TypeA:
00134       {
00135         Tag* c = new Tag( t, "activate" );
00136         c->setCData( m_jid.full() );
00137         break;
00138       }
00139       default:
00140         break;
00141     }
00142 
00143     return t;
00144   }
00145   // ---- ~SOCKS5BytestreamManager::Query ----
00146 
00147   // ---- SOCKS5BytestreamManager ----
00148   SOCKS5BytestreamManager::SOCKS5BytestreamManager( ClientBase* parent, BytestreamHandler* s5bh )
00149     : m_parent( parent ), m_socks5BytestreamHandler( s5bh ), m_server( 0 )
00150   {
00151     if( m_parent )
00152     {
00153       m_parent->registerStanzaExtension( new Query() );
00154       m_parent->registerIqHandler( this, ExtS5BQuery );
00155     }
00156   }
00157 
00158   SOCKS5BytestreamManager::~SOCKS5BytestreamManager()
00159   {
00160     if( m_parent )
00161     {
00162       m_parent->removeIqHandler( this, ExtS5BQuery );
00163       m_parent->removeIDHandler( this );
00164     }
00165 
00166     util::clearMap( m_s5bMap );
00167   }
00168 
00169   void SOCKS5BytestreamManager::addStreamHost( const JID& jid, const std::string& host, int port )
00170   {
00171     StreamHost sh;
00172     sh.jid = jid;
00173     sh.host = host;
00174     sh.port = port;
00175     m_hosts.push_back( sh );
00176   }
00177 
00178   bool SOCKS5BytestreamManager::requestSOCKS5Bytestream( const JID& to, S5BMode mode,
00179                                                          const std::string& sid,
00180                                                          const JID& from )
00181   {
00182     if( !m_parent )
00183     {
00184       m_parent->logInstance().warn( LogAreaClassS5BManager,
00185                                     "No parent (ClientBase) set, cannot request bytestream." );
00186       return false;
00187     }
00188 
00189     if( m_hosts.empty() )
00190     {
00191       m_parent->logInstance().warn( LogAreaClassS5BManager,
00192                                     "No stream hosts set, cannot request bytestream." );
00193       return false;
00194     }
00195 
00196     const std::string& msid = sid.empty() ? m_parent->getID() : sid;
00197     const std::string& id = m_parent->getID();
00198     IQ iq( IQ::Set, to, id );
00199     iq.addExtension( new Query( msid, mode, m_hosts ) );
00200     if( from )
00201       iq.setFrom( from );
00202 
00203     if( m_server )
00204     {
00205       SHA sha;
00206       sha.feed( msid );
00207       if( from )
00208         sha.feed( from.full() );
00209       else
00210         sha.feed( m_parent->jid().full() );
00211       sha.feed( to.full() );
00212       m_server->registerHash( sha.hex() );
00213     }
00214 
00215     AsyncS5BItem asi;
00216     asi.sHosts = m_hosts;
00217     asi.id = id;
00218     asi.from = to;
00219     asi.to = from ? from : m_parent->jid();
00220     asi.incoming = false;
00221     m_asyncTrackMap[msid] = asi;
00222 
00223     m_trackMap[id] = msid;
00224     m_parent->send( iq, this, S5BOpenStream );
00225 
00226     return true;
00227   }
00228 
00229   void SOCKS5BytestreamManager::acknowledgeStreamHost( bool success, const JID& jid,
00230                                                        const std::string& sid )
00231   {
00232     AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
00233     if( it == m_asyncTrackMap.end() || !m_parent )
00234       return;
00235 
00236     const AsyncS5BItem& item = (*it).second;
00237 
00238     IQ* iq = 0;
00239 
00240     if( item.incoming )
00241     {
00242       iq = new IQ( IQ::Result, item.from.full(), item.id );
00243       if( item.to )
00244         iq->setFrom( item.to );
00245 
00246       if( success )
00247         iq->addExtension( new Query( jid, sid, false ) );
00248       else
00249         iq->addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
00250 
00251       m_parent->send( *iq );
00252     }
00253     else
00254     {
00255       if( success )
00256       {
00257         const std::string& id = m_parent->getID();
00258         iq = new IQ( IQ::Set, jid.full(), id );
00259         iq->addExtension( new Query( item.from, sid, true ) );
00260 
00261         m_trackMap[id] = sid;
00262         m_parent->send( *iq, this, S5BActivateStream );
00263       }
00264     }
00265 
00266     delete iq;
00267   }
00268 
00269   bool SOCKS5BytestreamManager::handleIq( const IQ& iq )
00270   {
00271     const Query* q = iq.findExtension<Query>( ExtS5BQuery );
00272     if( !q || !m_socks5BytestreamHandler
00273         || m_trackMap.find( iq.id() ) != m_trackMap.end() )
00274       return false;
00275 
00276     switch( iq.subtype() )
00277     {
00278       case IQ::Set:
00279       {
00280         const std::string& sid = q->sid();
00281 // FIXME What is haveStream() good for?
00282         if( /*haveStream( iq.from() ) ||*/ sid.empty() || q->mode() == S5BUDP )
00283         {
00284           rejectSOCKS5Bytestream( iq.from(), iq.id(), StanzaErrorNotAcceptable );
00285           return true;
00286         }
00287         AsyncS5BItem asi;
00288         asi.sHosts = q->hosts();
00289         asi.id = iq.id();
00290         asi.from = iq.from();
00291         asi.to = iq.to();
00292         asi.incoming = true;
00293         m_asyncTrackMap[sid] = asi;
00294         m_socks5BytestreamHandler->handleIncomingBytestreamRequest( sid, iq.from() );
00295         break;
00296       }
00297       case IQ::Error:
00298         m_socks5BytestreamHandler->handleBytestreamError( iq, EmptyString );
00299         break;
00300       default:
00301         break;
00302     }
00303 
00304     return true;
00305   }
00306 
00307   const StreamHost* SOCKS5BytestreamManager::findProxy( const JID& from, const std::string& hostjid,
00308                                                         const std::string& sid )
00309   {
00310     AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
00311     if( it == m_asyncTrackMap.end() )
00312       return 0;
00313 
00314     if( (*it).second.from == from )
00315     {
00316       StreamHostList::const_iterator it2 = (*it).second.sHosts.begin();
00317       for( ; it2 != (*it).second.sHosts.end(); ++it2 )
00318       {
00319         if( (*it2).jid == hostjid )
00320         {
00321           return &(*it2);
00322         }
00323       }
00324     }
00325 
00326     return 0;
00327   }
00328 
00329   bool SOCKS5BytestreamManager::haveStream( const JID& from )
00330   {
00331     S5BMap::const_iterator it = m_s5bMap.begin();
00332     for( ; it != m_s5bMap.end(); ++it )
00333     {
00334       if( (*it).second && (*it).second->target() == from )
00335         return true;
00336     }
00337     return false;
00338   }
00339 
00340   void SOCKS5BytestreamManager::acceptSOCKS5Bytestream( const std::string& sid )
00341   {
00342     AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
00343     if( it == m_asyncTrackMap.end() || !m_socks5BytestreamHandler )
00344       return;
00345 
00346     SOCKS5Bytestream* s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
00347                                                   m_parent->logInstance(),
00348                                                   (*it).second.from, (*it).second.to, sid );
00349     s5b->setStreamHosts( (*it).second.sHosts );
00350     m_s5bMap[sid] = s5b;
00351     m_socks5BytestreamHandler->handleIncomingBytestream( s5b );
00352   }
00353 
00354   void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const std::string& sid, StanzaError reason )
00355   {
00356     AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
00357     if( it != m_asyncTrackMap.end() )
00358     {
00359       rejectSOCKS5Bytestream( (*it).second.from, (*it).second.id, reason );
00360       m_asyncTrackMap.erase( it );
00361     }
00362   }
00363 
00364   void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const JID& from,
00365                                                         const std::string& id,
00366                                                         StanzaError reason )
00367   {
00368     IQ iq( IQ::Error, from, id );
00369 
00370     switch( reason )
00371     {
00372       case StanzaErrorForbidden:
00373       case StanzaErrorNotAcceptable:
00374       {
00375         iq.addExtension( new Error( StanzaErrorTypeAuth, reason ) );
00376         break;
00377       }
00378       case StanzaErrorFeatureNotImplemented:
00379       case StanzaErrorNotAllowed:
00380       default:
00381       {
00382         iq.addExtension( new Error( StanzaErrorTypeCancel, reason ) );
00383         break;
00384       }
00385     }
00386 
00387     m_parent->send( iq );
00388   }
00389 
00390   void SOCKS5BytestreamManager::handleIqID( const IQ& iq, int context )
00391   {
00392     StringMap::iterator it = m_trackMap.find( iq.id() );
00393     if( it == m_trackMap.end() )
00394       return;
00395 
00396     switch( context )
00397     {
00398       case S5BOpenStream:
00399       {
00400         switch( iq.subtype() )
00401         {
00402           case IQ::Result:
00403           {
00404             const Query* q = iq.findExtension<Query>( ExtS5BQuery );
00405             if( q && m_socks5BytestreamHandler )
00406             {
00407               const std::string& proxy = q->jid().full();
00408               const StreamHost* sh = findProxy( iq.from(), proxy, (*it).second );
00409               if( sh )
00410               {
00411                 SOCKS5Bytestream* s5b = 0;
00412                 bool selfProxy = ( proxy == m_parent->jid().full() && m_server );
00413                 if( selfProxy )
00414                 {
00415                   SHA sha;
00416                   sha.feed( (*it).second );
00417                   sha.feed( iq.to().full() );
00418                   sha.feed( iq.from().full() );
00419                   s5b = new SOCKS5Bytestream( this, m_server->getConnection( sha.hex() ),
00420                                               m_parent->logInstance(),
00421                                                   iq.to(), iq.from(),
00422                                                       (*it).second );
00423                 }
00424                 else
00425                 {
00426                   s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
00427                                               m_parent->logInstance(),
00428                                                   iq.to(), iq.from(),
00429                                                       (*it).second );
00430                   s5b->setStreamHosts( StreamHostList( 1, *sh ) );
00431                 }
00432                 m_s5bMap[(*it).second] = s5b;
00433                 m_socks5BytestreamHandler->handleOutgoingBytestream( s5b );
00434                 if( selfProxy )
00435                   s5b->activate();
00436               }
00437             }
00438             break;
00439           }
00440           case IQ::Error:
00441             m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second );
00442             break;
00443           default:
00444             break;
00445         }
00446         break;
00447       }
00448       case S5BActivateStream:
00449       {
00450         switch( iq.subtype() )
00451         {
00452           case IQ::Result:
00453           {
00454             S5BMap::const_iterator it5 = m_s5bMap.find( (*it).second );
00455             if( it5 != m_s5bMap.end() )
00456               (*it5).second->activate();
00457             break;
00458           }
00459           case IQ::Error:
00460             m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second );
00461             break;
00462           default:
00463             break;
00464         }
00465         break;
00466       }
00467       default:
00468         break;
00469     }
00470     m_trackMap.erase( it );
00471   }
00472 
00473   bool SOCKS5BytestreamManager::dispose( SOCKS5Bytestream* s5b )
00474   {
00475     S5BMap::iterator it = m_s5bMap.find( s5b->sid() );
00476     if( it != m_s5bMap.end() )
00477     {
00478       delete s5b;
00479       m_s5bMap.erase( it );
00480       return true;
00481     }
00482 
00483     return false;
00484   }
00485 
00486 }