socks5bytestreammanager.cpp
/*
Copyright (c) 2006-2023 by Jakob Schröter <js@camaya.net>
This file is part of the gloox library. http://camaya.net/gloox
This software is distributed under a license. The full license
agreement can be found in the file LICENSE in this distribution.
This software may not be copied, modified, sold or distributed
other than expressed in the named license agreement.
This software is distributed without any warranty.
*/
#include "bytestreamhandler.h"
#include "socks5bytestreammanager.h"
#include "socks5bytestreamserver.h"
#include "socks5bytestream.h"
#include "clientbase.h"
#include "disco.h"
#include "error.h"
#include "connectionbase.h"
#include "sha.h"
#include "util.h"
#include <cstdlib>
namespace gloox
{
// ---- SOCKS5BytestreamManager::Query ----
static const char* s5bModeValues[] =
{
"tcp", "udp"
};
static inline const char* modeString( SOCKS5BytestreamManager::S5BMode mode )
{
return s5bModeValues[mode];
}
SOCKS5BytestreamManager::Query::Query()
: StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid )
{
}
SOCKS5BytestreamManager::Query::Query( const std::string& sid, S5BMode mode,
const StreamHostList& hosts )
: StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_mode( mode ), m_hosts( hosts ), m_type( TypeSH )
{
}
SOCKS5BytestreamManager::Query::Query( const JID& jid, const std::string& sid, bool activate )
: StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_jid( jid ), m_type( activate ? TypeA : TypeSHU )
{
}
SOCKS5BytestreamManager::Query::Query( const Tag* tag )
: StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid )
{
if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_BYTESTREAMS
/*|| !tag->hasAttribute( "sid" )*/ )
return;
m_sid = tag->findAttribute( "sid" );
m_mode = static_cast<S5BMode>( util::deflookup( tag->findAttribute( "mode" ), s5bModeValues, S5BTCP ) );
const TagList& l = tag->children();
TagList::const_iterator it = l.begin();
for( ; it != l.end(); ++it )
{
if( (*it)->name() == "streamhost" && (*it)->hasAttribute( "jid" )
&& (*it)->hasAttribute( "host" ) && (*it)->hasAttribute( "port" ) )
{
m_type = TypeSH;
StreamHost sh;
sh.jid = (*it)->findAttribute( "jid" );
sh.host = (*it)->findAttribute( "host" );
sh.port = atoi( (*it)->findAttribute( "port" ).c_str() );
m_hosts.push_back( sh );
}
else if( (*it)->name() == "streamhost-used" )
{
m_type = TypeSHU;
m_jid = (*it)->findAttribute( "jid" );
}
else if( (*it)->name() == "activate" )
{
m_type = TypeA;
m_jid = (*it)->cdata();
}
}
}
SOCKS5BytestreamManager::Query::~Query()
{
}
const std::string& SOCKS5BytestreamManager::Query::filterString() const
{
static const std::string filter = "/iq/query[@xmlns='" + XMLNS_BYTESTREAMS + "']";
return filter;
}
Tag* SOCKS5BytestreamManager::Query::tag() const
{
if( m_type == TypeInvalid /*|| m_sid.empty()*/ )
return 0;
Tag* t = new Tag( "query" );
t->setXmlns( XMLNS_BYTESTREAMS );
t->addAttribute( "sid", m_sid );
switch( m_type )
{
case TypeSH:
{
t->addAttribute( "mode", util::deflookup( m_mode, s5bModeValues, "tcp" ) );
StreamHostList::const_iterator it = m_hosts.begin();
for( ; it != m_hosts.end(); ++it )
{
Tag* s = new Tag( t, "streamhost" );
s->addAttribute( "jid", (*it).jid.full() );
s->addAttribute( "host", (*it).host );
s->addAttribute( "port", (*it).port );
}
break;
}
case TypeSHU:
{
Tag* s = new Tag( t, "streamhost-used" );
s->addAttribute( "jid", m_jid.full() );
break;
}
case TypeA:
{
Tag* c = new Tag( t, "activate" );
c->setCData( m_jid.full() );
break;
}
default:
break;
}
return t;
}
// ---- ~SOCKS5BytestreamManager::Query ----
// ---- SOCKS5BytestreamManager ----
SOCKS5BytestreamManager::SOCKS5BytestreamManager( ClientBase* parent, BytestreamHandler* s5bh )
: m_parent( parent ), m_socks5BytestreamHandler( s5bh ), m_server( 0 )
{
if( m_parent )
{
m_parent->registerStanzaExtension( new Query() );
m_parent->registerIqHandler( this, ExtS5BQuery );
}
}
SOCKS5BytestreamManager::~SOCKS5BytestreamManager()
{
if( m_parent )
{
m_parent->removeIqHandler( this, ExtS5BQuery );
m_parent->removeIDHandler( this );
}
util::clearMap( m_s5bMap );
}
void SOCKS5BytestreamManager::addStreamHost( const JID& jid, const std::string& host, int port )
{
StreamHost sh;
sh.jid = jid;
sh.host = host;
sh.port = port;
m_hosts.push_back( sh );
}
bool SOCKS5BytestreamManager::requestSOCKS5Bytestream( const JID& to, S5BMode mode,
const std::string& sid,
const JID& from )
{
if( !m_parent )
{
m_parent->logInstance().warn( LogAreaClassS5BManager,
"No parent (ClientBase) set, cannot request bytestream." );
return false;
}
if( m_hosts.empty() )
{
m_parent->logInstance().warn( LogAreaClassS5BManager,
"No stream hosts set, cannot request bytestream." );
return false;
}
const std::string& msid = sid.empty() ? m_parent->getID() : sid;
const std::string& id = m_parent->getID();
IQ iq( IQ::Set, to, id );
iq.addExtension( new Query( msid, mode, m_hosts ) );
if( from )
iq.setFrom( from );
if( m_server )
{
SHA sha;
sha.feed( msid );
if( from )
sha.feed( from.full() );
else
sha.feed( m_parent->jid().full() );
sha.feed( to.full() );
m_server->registerHash( sha.hex() );
}
AsyncS5BItem asi;
asi.sHosts = m_hosts;
asi.id = id;
asi.from = to;
asi.to = from ? from : m_parent->jid();
asi.incoming = false;
m_asyncTrackMap[msid] = asi;
m_trackMap[id] = msid;
m_parent->send( iq, this, S5BOpenStream );
return true;
}
void SOCKS5BytestreamManager::acknowledgeStreamHost( bool success, const JID& jid,
const std::string& sid )
{
AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
if( it == m_asyncTrackMap.end() || !m_parent )
return;
const AsyncS5BItem& item = (*it).second;
IQ* iq = 0;
if( item.incoming )
{
iq = new IQ( IQ::Result, item.from.full(), item.id );
if( item.to )
iq->setFrom( item.to );
if( success )
iq->addExtension( new Query( jid, sid, false ) );
else
iq->addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
m_parent->send( *iq );
}
else
{
if( success )
{
const std::string& id = m_parent->getID();
iq = new IQ( IQ::Set, jid.full(), id );
iq->addExtension( new Query( item.from, sid, true ) );
m_trackMap[id] = sid;
m_parent->send( *iq, this, S5BActivateStream );
}
}
delete iq;
}
bool SOCKS5BytestreamManager::handleIq( const IQ& iq )
{
const Query* q = iq.findExtension<Query>( ExtS5BQuery );
if( !q || !m_socks5BytestreamHandler
|| m_trackMap.find( iq.id() ) != m_trackMap.end() )
return false;
switch( iq.subtype() )
{
case IQ::Set:
{
const std::string& sid = q->sid();
// FIXME What is haveStream() good for?
if( /*haveStream( iq.from() ) ||*/ sid.empty() || q->mode() == S5BUDP )
{
rejectSOCKS5Bytestream( iq.from(), iq.id(), StanzaErrorNotAcceptable );
return true;
}
AsyncS5BItem asi;
asi.sHosts = q->hosts();
asi.id = iq.id();
asi.from = iq.from();
asi.to = iq.to();
asi.incoming = true;
m_asyncTrackMap[sid] = asi;
m_socks5BytestreamHandler->handleIncomingBytestreamRequest( sid, iq.from() );
break;
}
case IQ::Error:
m_socks5BytestreamHandler->handleBytestreamError( iq, EmptyString );
break;
default:
break;
}
return true;
}
const StreamHost* SOCKS5BytestreamManager::findProxy( const JID& from, const std::string& hostjid,
const std::string& sid )
{
AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
if( it == m_asyncTrackMap.end() )
return 0;
if( (*it).second.from == from )
{
StreamHostList::const_iterator it2 = (*it).second.sHosts.begin();
for( ; it2 != (*it).second.sHosts.end(); ++it2 )
{
if( (*it2).jid == hostjid )
{
return &(*it2);
}
}
}
return 0;
}
bool SOCKS5BytestreamManager::haveStream( const JID& from )
{
S5BMap::const_iterator it = m_s5bMap.begin();
for( ; it != m_s5bMap.end(); ++it )
{
if( (*it).second && (*it).second->target() == from )
return true;
}
return false;
}
void SOCKS5BytestreamManager::acceptSOCKS5Bytestream( const std::string& sid )
{
AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
if( it == m_asyncTrackMap.end() || !m_socks5BytestreamHandler )
return;
SOCKS5Bytestream* s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
m_parent->logInstance(),
(*it).second.from, (*it).second.to, sid );
s5b->setStreamHosts( (*it).second.sHosts );
m_s5bMap[sid] = s5b;
m_socks5BytestreamHandler->handleIncomingBytestream( s5b );
}
void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const std::string& sid, StanzaError reason )
{
AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
if( it != m_asyncTrackMap.end() )
{
rejectSOCKS5Bytestream( (*it).second.from, (*it).second.id, reason );
m_asyncTrackMap.erase( it );
}
}
void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const JID& from,
const std::string& id,
StanzaError reason )
{
IQ iq( IQ::Error, from, id );
switch( reason )
{
case StanzaErrorForbidden:
case StanzaErrorNotAcceptable:
{
iq.addExtension( new Error( StanzaErrorTypeAuth, reason ) );
break;
}
case StanzaErrorFeatureNotImplemented:
case StanzaErrorNotAllowed:
default:
{
iq.addExtension( new Error( StanzaErrorTypeCancel, reason ) );
break;
}
}
m_parent->send( iq );
}
void SOCKS5BytestreamManager::handleIqID( const IQ& iq, int context )
{
StringMap::iterator it = m_trackMap.find( iq.id() );
if( it == m_trackMap.end() )
return;
switch( context )
{
case S5BOpenStream:
{
switch( iq.subtype() )
{
case IQ::Result:
{
const Query* q = iq.findExtension<Query>( ExtS5BQuery );
if( q && m_socks5BytestreamHandler )
{
const std::string& proxy = q->jid().full();
const StreamHost* sh = findProxy( iq.from(), proxy, (*it).second );
if( sh )
{
SOCKS5Bytestream* s5b = 0;
bool selfProxy = ( proxy == m_parent->jid().full() && m_server );
if( selfProxy )
{
SHA sha;
sha.feed( (*it).second );
sha.feed( iq.to().full() );
sha.feed( iq.from().full() );
s5b = new SOCKS5Bytestream( this, m_server->getConnection( sha.hex() ),
m_parent->logInstance(),
iq.to(), iq.from(),
(*it).second );
}
else
{
s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
m_parent->logInstance(),
iq.to(), iq.from(),
(*it).second );
s5b->setStreamHosts( StreamHostList( 1, *sh ) );
}
m_s5bMap[(*it).second] = s5b;
m_socks5BytestreamHandler->handleOutgoingBytestream( s5b );
if( selfProxy )
s5b->activate();
}
}
break;
}
case IQ::Error:
m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second );
break;
default:
break;
}
break;
}
case S5BActivateStream:
{
switch( iq.subtype() )
{
case IQ::Result:
{
S5BMap::const_iterator it5 = m_s5bMap.find( (*it).second );
if( it5 != m_s5bMap.end() )
(*it5).second->activate();
break;
}
case IQ::Error:
m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second );
break;
default:
break;
}
break;
}
default:
break;
}
m_trackMap.erase( it );
}
bool SOCKS5BytestreamManager::dispose( SOCKS5Bytestream* s5b )
{
S5BMap::iterator it = m_s5bMap.find( s5b->sid() );
if( it != m_s5bMap.end() )
{
delete s5b;
m_s5bMap.erase( it );
return true;
}
return false;
}
}
gloox::BytestreamHandler
A virtual interface that allows to receive new incoming Bytestream requests from remote entities.
Definition bytestreamhandler.h:37
gloox::Bytestream::sid
const std::string & sid() const
Definition bytestream.h:114
gloox::ClientBase
This is the common base class for a Jabber/XMPP Client and a Jabber Component.
Definition clientbase.h:79
gloox::ClientBase::getID
const std::string getID()
Definition clientbase.cpp:1168
gloox::ClientBase::send
void send(Tag *tag)
Definition clientbase.cpp:1004
gloox::Error
A stanza error abstraction implemented as a StanzaExtension.
Definition error.h:35
gloox::IQ
An abstraction of an IQ stanza.
Definition iq.h:34
gloox::IQ::subtype
IqType subtype() const
Definition iq.h:74
gloox::IQ::Set
@ Set
Definition iq.h:46
gloox::IQ::Error
@ Error
Definition iq.h:49
gloox::IQ::Result
@ Result
Definition iq.h:48
gloox::JID
An abstraction of a JID.
Definition jid.h:31
gloox::JID::full
const std::string & full() const
Definition jid.h:61
gloox::SHA
An implementation of SHA1.
Definition sha.h:30
gloox::SHA::feed
void feed(const unsigned char *data, unsigned length)
Definition sha.cpp:89
gloox::SHA::hex
const std::string hex()
Definition sha.cpp:53
gloox::SOCKS5BytestreamManager::~SOCKS5BytestreamManager
virtual ~SOCKS5BytestreamManager()
Definition socks5bytestreammanager.cpp:158
gloox::SOCKS5BytestreamManager::SOCKS5BytestreamManager
SOCKS5BytestreamManager(ClientBase *parent, BytestreamHandler *s5bh)
Definition socks5bytestreammanager.cpp:148
gloox::SOCKS5BytestreamManager::handleIqID
virtual void handleIqID(const IQ &iq, int context)
Definition socks5bytestreammanager.cpp:390
gloox::SOCKS5BytestreamManager::handleIq
virtual bool handleIq(const IQ &iq)
Definition socks5bytestreammanager.cpp:269
gloox::SOCKS5BytestreamManager::addStreamHost
void addStreamHost(const JID &jid, const std::string &host, int port)
Definition socks5bytestreammanager.cpp:169
gloox::SOCKS5BytestreamManager::rejectSOCKS5Bytestream
void rejectSOCKS5Bytestream(const std::string &sid, StanzaError reason=StanzaErrorNotAcceptable)
Definition socks5bytestreammanager.cpp:354
gloox::SOCKS5BytestreamManager::S5BMode
S5BMode
Definition socks5bytestreammanager.h:62
gloox::SOCKS5BytestreamManager::S5BUDP
@ S5BUDP
Definition socks5bytestreammanager.h:64
gloox::SOCKS5BytestreamManager::requestSOCKS5Bytestream
bool requestSOCKS5Bytestream(const JID &to, S5BMode mode, const std::string &sid=EmptyString, const JID &from=JID())
Definition socks5bytestreammanager.cpp:178
gloox::SOCKS5BytestreamManager::dispose
bool dispose(SOCKS5Bytestream *s5b)
Definition socks5bytestreammanager.cpp:473
gloox::SOCKS5BytestreamManager::acceptSOCKS5Bytestream
void acceptSOCKS5Bytestream(const std::string &sid)
Definition socks5bytestreammanager.cpp:340
gloox::SOCKS5Bytestream::setStreamHosts
void setStreamHosts(const StreamHostList &hosts)
Definition socks5bytestream.h:107
gloox::StanzaExtension
This class abstracts a stanza extension, which is usually an XML child element in a specific namespac...
Definition stanzaextension.h:204
gloox::Stanza::addExtension
void addExtension(const StanzaExtension *se)
Definition stanza.cpp:52
gloox::Stanza::setFrom
void setFrom(const JID &from)
Definition stanza.h:45
gloox::Stanza::id
const std::string & id() const
Definition stanza.h:63
gloox::Stanza::from
const JID & from() const
Definition stanza.h:51
gloox::Stanza::to
const JID & to() const
Definition stanza.h:57
gloox::Stanza::findExtension
const StanzaExtension * findExtension(int type) const
Definition stanza.cpp:57
gloox::util::clearMap
void clearMap(std::map< Key, T * > &M)
Definition util.h:169
gloox
The namespace for the gloox library.
Definition adhoc.cpp:28
gloox::LogAreaClassS5BManager
@ LogAreaClassS5BManager
Definition gloox.h:1064
gloox::EmptyString
const std::string EmptyString
Definition gloox.cpp:124
gloox::StanzaError
StanzaError
Definition gloox.h:872
gloox::StanzaErrorItemNotFound
@ StanzaErrorItemNotFound
Definition gloox.h:893
gloox::StanzaErrorFeatureNotImplemented
@ StanzaErrorFeatureNotImplemented
Definition gloox.h:881
gloox::StanzaErrorNotAllowed
@ StanzaErrorNotAllowed
Definition gloox.h:904
gloox::StanzaErrorForbidden
@ StanzaErrorForbidden
Definition gloox.h:884
gloox::StanzaErrorNotAcceptable
@ StanzaErrorNotAcceptable
Definition gloox.h:900
gloox::StanzaErrorTypeAuth
@ StanzaErrorTypeAuth
Definition gloox.h:858
gloox::StanzaErrorTypeCancel
@ StanzaErrorTypeCancel
Definition gloox.h:859
gloox::ExtS5BQuery
@ ExtS5BQuery
Definition stanzaextension.h:83
gloox::TypeInvalid
@ TypeInvalid
Definition dataform.h:44
gloox::StreamHostList
std::list< StreamHost > StreamHostList
Definition socks5bytestreammanager.h:43
gloox::XMLNS_BYTESTREAMS
const std::string XMLNS_BYTESTREAMS
Definition gloox.cpp:66
gloox::StreamHost
Definition socks5bytestreammanager.h:33
gloox::StreamHost::port
int port
Definition socks5bytestreammanager.h:36
gloox::StreamHost::host
std::string host
Definition socks5bytestreammanager.h:35
gloox::StreamHost::jid
JID jid
Definition socks5bytestreammanager.h:34