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
27namespace 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
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
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()
LogSink & logInstance()
Definition clientbase.h:599
void removeIqHandler(IqHandler *ih, int exttype)
void removeIDHandler(IqHandler *ih)
ConnectionBase * connectionImpl() const
Definition clientbase.h:317
void send(Tag *tag)
const JID & jid()
Definition clientbase.h:147
void registerIqHandler(IqHandler *ih, int exttype)
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())
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 std::string & id() const
Definition stanza.h:63
const JID & from() const
Definition stanza.h:51
const JID & to() const
Definition stanza.h:57
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:31
@ 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