gloox  1.0.28
socks5bytestreammanager.cpp
1 /*
2  Copyright (c) 2006-2023 by Jakob Schröter <js@camaya.net>
3  This file is part of the gloox library. http://camaya.net/gloox
4 
5  This software is distributed under a license. The full license
6  agreement can be found in the file LICENSE in this distribution.
7  This software may not be copied, modified, sold or distributed
8  other than expressed in the named license agreement.
9 
10  This software is distributed without any warranty.
11 */
12 
13 
14 #include "bytestreamhandler.h"
15 #include "socks5bytestreammanager.h"
16 #include "socks5bytestreamserver.h"
17 #include "socks5bytestream.h"
18 #include "clientbase.h"
19 #include "disco.h"
20 #include "error.h"
21 #include "connectionbase.h"
22 #include "sha.h"
23 #include "util.h"
24 
25 #include <cstdlib>
26 
27 namespace gloox
28 {
29 
30  // ---- SOCKS5BytestreamManager::Query ----
31  static const char* s5bModeValues[] =
32  {
33  "tcp", "udp"
34  };
35 
36  static inline const char* modeString( SOCKS5BytestreamManager::S5BMode mode )
37  {
38  return s5bModeValues[mode];
39  }
40 
41  SOCKS5BytestreamManager::Query::Query()
42  : StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid )
43  {
44  }
45 
46  SOCKS5BytestreamManager::Query::Query( const std::string& sid, S5BMode mode,
47  const StreamHostList& hosts )
48  : StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_mode( mode ), m_hosts( hosts ), m_type( TypeSH )
49  {
50  }
51 
52  SOCKS5BytestreamManager::Query::Query( const JID& jid, const std::string& sid, bool activate )
53  : StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_jid( jid ), m_type( activate ? TypeA : TypeSHU )
54  {
55  }
56 
57  SOCKS5BytestreamManager::Query::Query( const Tag* tag )
58  : StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid )
59  {
60  if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_BYTESTREAMS
61  /*|| !tag->hasAttribute( "sid" )*/ )
62  return;
63 
64  m_sid = tag->findAttribute( "sid" );
65  m_mode = static_cast<S5BMode>( util::deflookup( tag->findAttribute( "mode" ), s5bModeValues, S5BTCP ) );
66 
67  const TagList& l = tag->children();
68  TagList::const_iterator it = l.begin();
69  for( ; it != l.end(); ++it )
70  {
71  if( (*it)->name() == "streamhost" && (*it)->hasAttribute( "jid" )
72  && (*it)->hasAttribute( "host" ) && (*it)->hasAttribute( "port" ) )
73  {
74  m_type = TypeSH;
75  StreamHost sh;
76  sh.jid = (*it)->findAttribute( "jid" );
77  sh.host = (*it)->findAttribute( "host" );
78  sh.port = atoi( (*it)->findAttribute( "port" ).c_str() );
79  m_hosts.push_back( sh );
80  }
81  else if( (*it)->name() == "streamhost-used" )
82  {
83  m_type = TypeSHU;
84  m_jid = (*it)->findAttribute( "jid" );
85  }
86  else if( (*it)->name() == "activate" )
87  {
88  m_type = TypeA;
89  m_jid = (*it)->cdata();
90  }
91  }
92  }
93 
94  SOCKS5BytestreamManager::Query::~Query()
95  {
96  }
97 
98  const std::string& SOCKS5BytestreamManager::Query::filterString() const
99  {
100  static const std::string filter = "/iq/query[@xmlns='" + XMLNS_BYTESTREAMS + "']";
101  return filter;
102  }
103 
104  Tag* SOCKS5BytestreamManager::Query::tag() const
105  {
106  if( m_type == TypeInvalid /*|| m_sid.empty()*/ )
107  return 0;
108 
109  Tag* t = new Tag( "query" );
110  t->setXmlns( XMLNS_BYTESTREAMS );
111  t->addAttribute( "sid", m_sid );
112  switch( m_type )
113  {
114  case TypeSH:
115  {
116  t->addAttribute( "mode", util::deflookup( m_mode, s5bModeValues, "tcp" ) );
117  StreamHostList::const_iterator it = m_hosts.begin();
118  for( ; it != m_hosts.end(); ++it )
119  {
120  Tag* s = new Tag( t, "streamhost" );
121  s->addAttribute( "jid", (*it).jid.full() );
122  s->addAttribute( "host", (*it).host );
123  s->addAttribute( "port", (*it).port );
124  }
125  break;
126  }
127  case TypeSHU:
128  {
129  Tag* s = new Tag( t, "streamhost-used" );
130  s->addAttribute( "jid", m_jid.full() );
131  break;
132  }
133  case TypeA:
134  {
135  Tag* c = new Tag( t, "activate" );
136  c->setCData( m_jid.full() );
137  break;
138  }
139  default:
140  break;
141  }
142 
143  return t;
144  }
145  // ---- ~SOCKS5BytestreamManager::Query ----
146 
147  // ---- SOCKS5BytestreamManager ----
148  SOCKS5BytestreamManager::SOCKS5BytestreamManager( ClientBase* parent, BytestreamHandler* s5bh )
149  : m_parent( parent ), m_socks5BytestreamHandler( s5bh ), m_server( 0 )
150  {
151  if( m_parent )
152  {
153  m_parent->registerStanzaExtension( new Query() );
154  m_parent->registerIqHandler( this, ExtS5BQuery );
155  }
156  }
157 
159  {
160  if( m_parent )
161  {
162  m_parent->removeIqHandler( this, ExtS5BQuery );
163  m_parent->removeIDHandler( this );
164  }
165 
166  util::clearMap( m_s5bMap );
167  }
168 
169  void SOCKS5BytestreamManager::addStreamHost( const JID& jid, const std::string& host, int port )
170  {
171  StreamHost sh;
172  sh.jid = jid;
173  sh.host = host;
174  sh.port = port;
175  m_hosts.push_back( sh );
176  }
177 
179  const std::string& sid,
180  const JID& from )
181  {
182  if( !m_parent )
183  {
185  "No parent (ClientBase) set, cannot request bytestream." );
186  return false;
187  }
188 
189  if( m_hosts.empty() )
190  {
192  "No stream hosts set, cannot request bytestream." );
193  return false;
194  }
195 
196  const std::string& msid = sid.empty() ? m_parent->getID() : sid;
197  const std::string& id = m_parent->getID();
198  IQ iq( IQ::Set, to, id );
199  iq.addExtension( new Query( msid, mode, m_hosts ) );
200  if( from )
201  iq.setFrom( from );
202 
203  if( m_server )
204  {
205  SHA sha;
206  sha.feed( msid );
207  if( from )
208  sha.feed( from.full() );
209  else
210  sha.feed( m_parent->jid().full() );
211  sha.feed( to.full() );
212  m_server->registerHash( sha.hex() );
213  }
214 
215  AsyncS5BItem asi;
216  asi.sHosts = m_hosts;
217  asi.id = id;
218  asi.from = to;
219  asi.to = from ? from : m_parent->jid();
220  asi.incoming = false;
221  m_asyncTrackMap[msid] = asi;
222 
223  m_trackMap[id] = msid;
224  m_parent->send( iq, this, S5BOpenStream );
225 
226  return true;
227  }
228 
229  void SOCKS5BytestreamManager::acknowledgeStreamHost( bool success, const JID& jid,
230  const std::string& sid )
231  {
232  AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
233  if( it == m_asyncTrackMap.end() || !m_parent )
234  return;
235 
236  const AsyncS5BItem& item = (*it).second;
237 
238  IQ* iq = 0;
239 
240  if( item.incoming )
241  {
242  iq = new IQ( IQ::Result, item.from.full(), item.id );
243  if( item.to )
244  iq->setFrom( item.to );
245 
246  if( success )
247  iq->addExtension( new Query( jid, sid, false ) );
248  else
250 
251  m_parent->send( *iq );
252  }
253  else
254  {
255  if( success )
256  {
257  const std::string& id = m_parent->getID();
258  iq = new IQ( IQ::Set, jid.full(), id );
259  iq->addExtension( new Query( item.from, sid, true ) );
260 
261  m_trackMap[id] = sid;
262  m_parent->send( *iq, this, S5BActivateStream );
263  }
264  }
265 
266  delete iq;
267  }
268 
270  {
271  const Query* q = iq.findExtension<Query>( ExtS5BQuery );
272  if( !q || !m_socks5BytestreamHandler
273  || m_trackMap.find( iq.id() ) != m_trackMap.end() )
274  return false;
275 
276  switch( iq.subtype() )
277  {
278  case IQ::Set:
279  {
280  const std::string& sid = q->sid();
281 // FIXME What is haveStream() good for?
282  if( /*haveStream( iq.from() ) ||*/ sid.empty() || q->mode() == S5BUDP )
283  {
285  return true;
286  }
287  AsyncS5BItem asi;
288  asi.sHosts = q->hosts();
289  asi.id = iq.id();
290  asi.from = iq.from();
291  asi.to = iq.to();
292  asi.incoming = true;
293  m_asyncTrackMap[sid] = asi;
294  m_socks5BytestreamHandler->handleIncomingBytestreamRequest( sid, iq.from() );
295  break;
296  }
297  case IQ::Error:
298  m_socks5BytestreamHandler->handleBytestreamError( iq, EmptyString );
299  break;
300  default:
301  break;
302  }
303 
304  return true;
305  }
306 
307  const StreamHost* SOCKS5BytestreamManager::findProxy( const JID& from, const std::string& hostjid,
308  const std::string& sid )
309  {
310  AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
311  if( it == m_asyncTrackMap.end() )
312  return 0;
313 
314  if( (*it).second.from == from )
315  {
316  StreamHostList::const_iterator it2 = (*it).second.sHosts.begin();
317  for( ; it2 != (*it).second.sHosts.end(); ++it2 )
318  {
319  if( (*it2).jid == hostjid )
320  {
321  return &(*it2);
322  }
323  }
324  }
325 
326  return 0;
327  }
328 
329  bool SOCKS5BytestreamManager::haveStream( const JID& from )
330  {
331  S5BMap::const_iterator it = m_s5bMap.begin();
332  for( ; it != m_s5bMap.end(); ++it )
333  {
334  if( (*it).second && (*it).second->target() == from )
335  return true;
336  }
337  return false;
338  }
339 
340  void SOCKS5BytestreamManager::acceptSOCKS5Bytestream( const std::string& sid )
341  {
342  AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
343  if( it == m_asyncTrackMap.end() || !m_socks5BytestreamHandler )
344  return;
345 
346  SOCKS5Bytestream* s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
347  m_parent->logInstance(),
348  (*it).second.from, (*it).second.to, sid );
349  s5b->setStreamHosts( (*it).second.sHosts );
350  m_s5bMap[sid] = s5b;
351  m_socks5BytestreamHandler->handleIncomingBytestream( s5b );
352  }
353 
354  void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const std::string& sid, StanzaError reason )
355  {
356  AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
357  if( it != m_asyncTrackMap.end() )
358  {
359  rejectSOCKS5Bytestream( (*it).second.from, (*it).second.id, reason );
360  m_asyncTrackMap.erase( it );
361  }
362  }
363 
365  const std::string& id,
366  StanzaError reason )
367  {
368  IQ iq( IQ::Error, from, id );
369 
370  switch( reason )
371  {
374  {
375  iq.addExtension( new Error( StanzaErrorTypeAuth, reason ) );
376  break;
377  }
380  default:
381  {
382  iq.addExtension( new Error( StanzaErrorTypeCancel, reason ) );
383  break;
384  }
385  }
386 
387  m_parent->send( iq );
388  }
389 
390  void SOCKS5BytestreamManager::handleIqID( const IQ& iq, int context )
391  {
392  StringMap::iterator it = m_trackMap.find( iq.id() );
393  if( it == m_trackMap.end() )
394  return;
395 
396  switch( context )
397  {
398  case S5BOpenStream:
399  {
400  switch( iq.subtype() )
401  {
402  case IQ::Result:
403  {
404  const Query* q = iq.findExtension<Query>( ExtS5BQuery );
405  if( q && m_socks5BytestreamHandler )
406  {
407  const std::string& proxy = q->jid().full();
408  const StreamHost* sh = findProxy( iq.from(), proxy, (*it).second );
409  if( sh )
410  {
411  SOCKS5Bytestream* s5b = 0;
412  bool selfProxy = ( proxy == m_parent->jid().full() && m_server );
413  if( selfProxy )
414  {
415  SHA sha;
416  sha.feed( (*it).second );
417  sha.feed( iq.to().full() );
418  sha.feed( iq.from().full() );
419  s5b = new SOCKS5Bytestream( this, m_server->getConnection( sha.hex() ),
420  m_parent->logInstance(),
421  iq.to(), iq.from(),
422  (*it).second );
423  }
424  else
425  {
426  s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
427  m_parent->logInstance(),
428  iq.to(), iq.from(),
429  (*it).second );
430  s5b->setStreamHosts( StreamHostList( 1, *sh ) );
431  }
432  m_s5bMap[(*it).second] = s5b;
433  m_socks5BytestreamHandler->handleOutgoingBytestream( s5b );
434  if( selfProxy )
435  s5b->activate();
436  }
437  }
438  break;
439  }
440  case IQ::Error:
441  m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second );
442  break;
443  default:
444  break;
445  }
446  break;
447  }
448  case S5BActivateStream:
449  {
450  switch( iq.subtype() )
451  {
452  case IQ::Result:
453  {
454  S5BMap::const_iterator it5 = m_s5bMap.find( (*it).second );
455  if( it5 != m_s5bMap.end() )
456  (*it5).second->activate();
457  break;
458  }
459  case IQ::Error:
460  m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second );
461  break;
462  default:
463  break;
464  }
465  break;
466  }
467  default:
468  break;
469  }
470  m_trackMap.erase( it );
471  }
472 
474  {
475  S5BMap::iterator it = m_s5bMap.find( s5b->sid() );
476  if( it != m_s5bMap.end() )
477  {
478  delete s5b;
479  m_s5bMap.erase( it );
480  return true;
481  }
482 
483  return false;
484  }
485 
486 }
A virtual interface that allows to receive new incoming Bytestream requests from remote entities.
virtual void handleIncomingBytestream(Bytestream *bs)=0
virtual void handleBytestreamError(const IQ &iq, const std::string &sid)=0
virtual void handleOutgoingBytestream(Bytestream *bs)=0
virtual void handleIncomingBytestreamRequest(const std::string &sid, const JID &from)=0
const std::string & sid() const
Definition: bytestream.h:114
This is the common base class for a Jabber/XMPP Client and a Jabber Component.
Definition: clientbase.h:79
const std::string getID()
void removeIqHandler(IqHandler *ih, int exttype)
const JID & jid()
Definition: clientbase.h:147
void removeIDHandler(IqHandler *ih)
LogSink & logInstance()
Definition: clientbase.h:599
void send(Tag *tag)
void registerIqHandler(IqHandler *ih, int exttype)
ConnectionBase * connectionImpl() const
Definition: clientbase.h:317
void registerStanzaExtension(StanzaExtension *ext)
virtual ConnectionBase * newInstance() const =0
A stanza error abstraction implemented as a StanzaExtension.
Definition: error.h:35
An abstraction of an IQ stanza.
Definition: iq.h:34
IqType subtype() const
Definition: iq.h:74
@ Set
Definition: iq.h:46
@ Error
Definition: iq.h:49
@ Result
Definition: iq.h:48
An abstraction of a JID.
Definition: jid.h:31
const std::string & full() const
Definition: jid.h:61
void warn(LogArea area, const std::string &message) const
Definition: logsink.h:75
An implementation of SHA1.
Definition: sha.h:30
void feed(const unsigned char *data, unsigned length)
Definition: sha.cpp:89
const std::string hex()
Definition: sha.cpp:53
virtual void handleIqID(const IQ &iq, int context)
void addStreamHost(const JID &jid, const std::string &host, int port)
void rejectSOCKS5Bytestream(const std::string &sid, StanzaError reason=StanzaErrorNotAcceptable)
bool requestSOCKS5Bytestream(const JID &to, S5BMode mode, const std::string &sid=EmptyString, const JID &from=JID())
bool dispose(SOCKS5Bytestream *s5b)
void acceptSOCKS5Bytestream(const std::string &sid)
An implementation of a single SOCKS5 Bytestream (XEP-0065).
void setStreamHosts(const StreamHostList &hosts)
void addExtension(const StanzaExtension *se)
Definition: stanza.cpp:52
void setFrom(const JID &from)
Definition: stanza.h:45
const JID & to() const
Definition: stanza.h:57
const std::string & id() const
Definition: stanza.h:63
const JID & from() const
Definition: stanza.h:51
const StanzaExtension * findExtension(int type) const
Definition: stanza.cpp:57
void clearMap(std::map< Key, T * > &M)
Definition: util.h:169
The namespace for the gloox library.
Definition: adhoc.cpp:28
std::list< Tag * > TagList
Definition: tag.h:26
@ LogAreaClassS5BManager
Definition: gloox.h:1064
const std::string EmptyString
Definition: gloox.cpp:124
StanzaError
Definition: gloox.h:872
@ StanzaErrorItemNotFound
Definition: gloox.h:893
@ StanzaErrorFeatureNotImplemented
Definition: gloox.h:881
@ StanzaErrorNotAllowed
Definition: gloox.h:904
@ StanzaErrorForbidden
Definition: gloox.h:884
@ StanzaErrorNotAcceptable
Definition: gloox.h:900
@ StanzaErrorTypeAuth
Definition: gloox.h:858
@ StanzaErrorTypeCancel
Definition: gloox.h:859
@ TypeInvalid
Definition: dataform.h:44
std::list< StreamHost > StreamHostList
const std::string XMLNS_BYTESTREAMS
Definition: gloox.cpp:66