gloox  0.9.9.12
socks5bytestreammanager.cpp
1 /*
2  Copyright (c) 2006-2008 by Jakob Schroeter <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 "socks5bytestreammanager.h"
15 #include "socks5bytestreamhandler.h"
16 #include "socks5bytestreamserver.h"
17 #include "socks5bytestream.h"
18 #include "clientbase.h"
19 #include "disco.h"
20 #include "connectionbase.h"
21 #include "sha.h"
22 
23 #include <cstdlib>
24 
25 namespace gloox
26 {
27 
29  : m_parent( parent ), m_socks5BytestreamHandler( s5bh ), m_server( 0 )
30  {
31  if( m_parent )
32  m_parent->registerIqHandler( this, XMLNS_BYTESTREAMS );
33  }
34 
36  {
37  if( m_parent )
38  {
40  m_parent->removeIDHandler( this );
41  }
42 
43  S5BMap::iterator it = m_s5bMap.begin();
44  for( ; it != m_s5bMap.end(); ++it )
45  {
46  delete (*it).second;
47  (*it).second = 0;
48  }
49  }
50 
51  void SOCKS5BytestreamManager::addStreamHost( const JID& jid, const std::string& host, int port )
52  {
53  StreamHost sh;
54  sh.jid = jid;
55  sh.host = host;
56  sh.port = port;
57  m_hosts.push_back( sh );
58  }
59 
61  const std::string& sid )
62  {
63  if( !m_parent )
64  return false;
65 
66  if( m_hosts.empty() )
67  {
69  "No stream hosts set, cannot request bytestream." );
70  return false;
71  }
72 
73  const std::string& msid = sid.empty() ? m_parent->getID() : sid;
74  const std::string& id = m_parent->getID();
75  Tag *iq = new Tag( "iq" );
76  iq->addAttribute( "type", "set" );
77  iq->addAttribute( "to", to.full() );
78  iq->addAttribute( "id", id );
79  Tag *q = new Tag( iq, "query", "xmlns", XMLNS_BYTESTREAMS );
80  q->addAttribute( "sid", msid );
81  q->addAttribute( "mode", /*( mode == S5BTCP ) ?*/ "tcp" /*: "udp"*/ );
82 
83  StreamHostList::const_iterator it = m_hosts.begin();
84  for( ; it != m_hosts.end(); ++it )
85  {
86  Tag* s = new Tag( q, "streamhost", "jid", (*it).jid.full() );
87  s->addAttribute( "host", (*it).host );
88  s->addAttribute( "port", (*it).port );
89  }
90 
91  if( m_server )
92  {
93  SHA sha;
94  sha.feed( msid );
95  sha.feed( m_parent->jid().full() );
96  sha.feed( to.full() );
97  m_server->registerHash( sha.hex() );
98  }
99 
100  AsyncS5BItem asi;
101  asi.sHosts = m_hosts;
102  asi.id = id;
103  asi.from = to;
104  asi.incoming = false;
105  m_asyncTrackMap[msid] = asi;
106 
107  m_trackMap[id] = msid;
108  m_parent->trackID( this, id, S5BOpenStream );
109  m_parent->send( iq );
110 
111  return true;
112  }
113 
114  void SOCKS5BytestreamManager::acknowledgeStreamHost( bool success, const JID& jid,
115  const std::string& sid )
116  {
117  AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
118  if( it == m_asyncTrackMap.end() || !m_parent )
119  return;
120 
121  Tag *iq = new Tag( "iq" );
122 
123  if( (*it).second.incoming )
124  {
125  iq->addAttribute( "to", (*it).second.from.full() );
126  iq->addAttribute( "id", (*it).second.id );
127 
128  if( success )
129  {
130  iq->addAttribute( "type", "result" );
131  Tag* q = new Tag( iq, "query", "xmlns", XMLNS_BYTESTREAMS );
132  new Tag( q, "streamhost-used", "jid", jid.full() );
133  }
134  else
135  {
136  iq->addAttribute( "type", "error" );
137  Tag* e = new Tag( iq, "error" );
138  e->addAttribute( "code", "404" );
139  e->addAttribute( "type", "cancel" );
140  new Tag( e, "item-not-found", "xmlns", XMLNS_XMPP_STANZAS );
141  }
142  }
143  else
144  {
145  if( success )
146  {
147  const std::string& id = m_parent->getID();
148  iq->addAttribute( "to", jid.full() );
149  iq->addAttribute( "id", id );
150  iq->addAttribute( "type", "set" );
151  Tag* q = new Tag( iq, "query", "xmlns", XMLNS_BYTESTREAMS );
152  q->addAttribute( "sid", sid );
153  new Tag( q, "activate", (*it).second.from.full() );
154 
155  m_trackMap[id] = sid;
156  m_parent->trackID( this, id, S5BActivateStream );
157  }
158  }
159 
160  m_parent->send( iq );
161  }
162 
164  {
165  Tag* q = stanza->findChild( "query", "xmlns", XMLNS_BYTESTREAMS );
166  if( !q || !m_socks5BytestreamHandler )
167  return false;
168 
169  if( m_trackMap.find( stanza->id() ) != m_trackMap.end() )
170  return false;
171 
172  switch( stanza->subtype() )
173  {
174  case StanzaIqSet:
175  {
176  const std::string& sid = q->findAttribute( "sid" );
177  const std::string& mode = q->findAttribute( "mode" );
178  if( haveStream( stanza->from() ) || sid.empty() || mode == "udp" )
179  {
180  rejectSOCKS5Bytestream( stanza->from(), stanza->id(), StanzaErrorNotAcceptable );
181  return true;
182  }
183  AsyncS5BItem asi;
184  Tag::TagList& l = q->children();
185  Tag::TagList::const_iterator it = l.begin();
186  for( ; it != l.end(); ++it )
187  {
188  if( (*it)->name() == "streamhost" && (*it)->hasAttribute( "jid" )
189  && (*it)->hasAttribute( "host" ) && (*it)->hasAttribute( "port" ) )
190  {
191  StreamHost sh;
192  sh.jid = (*it)->findAttribute( "jid" );
193  sh.host = (*it)->findAttribute( "host" );
194  sh.port = atoi( (*it)->findAttribute( "port" ).c_str() );
195  asi.sHosts.push_back( sh );
196  }
197  }
198  asi.id = stanza->id();
199  asi.from = stanza->from();
200  asi.incoming = true;
201  m_asyncTrackMap[sid] = asi;
202  m_socks5BytestreamHandler->handleIncomingSOCKS5BytestreamRequest( sid, stanza->from() );
203  break;
204  }
205  case StanzaIqError:
206  m_socks5BytestreamHandler->handleSOCKS5BytestreamError( stanza, std::string() );
207  break;
208  default:
209  break;
210  }
211 
212  return true;
213  }
214 
215  const StreamHost* SOCKS5BytestreamManager::findProxy( const JID& from, const std::string& hostjid,
216  const std::string& sid )
217  {
218  AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid );
219  if( it == m_asyncTrackMap.end() )
220  return 0;
221 
222  if( (*it).second.from == from )
223  {
224  StreamHostList::const_iterator it2 = (*it).second.sHosts.begin();
225  for( ; it2 != (*it).second.sHosts.end(); ++it2 )
226  {
227  if( (*it2).jid == hostjid )
228  {
229  return &(*it2);
230  }
231  }
232  }
233 
234  return 0;
235  }
236 
237  bool SOCKS5BytestreamManager::haveStream( const JID& from )
238  {
239  S5BMap::const_iterator it = m_s5bMap.begin();
240  for( ; it != m_s5bMap.end(); ++it )
241  {
242  if( (*it).second && (*it).second->target() == from )
243  return true;
244  }
245  return false;
246  }
247 
248  void SOCKS5BytestreamManager::acceptSOCKS5Bytestream( const std::string& sid )
249  {
250  AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
251  if( it == m_asyncTrackMap.end() || !m_socks5BytestreamHandler )
252  return;
253 
254  SOCKS5Bytestream* s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
255  m_parent->logInstance(),
256  (*it).second.from, m_parent->jid(), sid );
257  s5b->setStreamHosts( (*it).second.sHosts );
258  m_s5bMap[sid] = s5b;
259  m_socks5BytestreamHandler->handleIncomingSOCKS5Bytestream( s5b );
260  }
261 
262  void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const std::string& sid )
263  {
264  AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid );
265  if( it != m_asyncTrackMap.end() )
266  {
267  rejectSOCKS5Bytestream( (*it).second.from, (*it).second.id, StanzaErrorNotAcceptable );
268  m_asyncTrackMap.erase( it );
269  }
270  }
271 
272  void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const JID& from, const std::string& id,
273  StanzaError reason )
274  {
275  Tag *iq = new Tag( "iq" );
276  iq->addAttribute( "type", "error" );
277  iq->addAttribute( "to", from.full() );
278  iq->addAttribute( "id", id );
279  Tag *e = new Tag( iq, "error" );
280  switch( reason )
281  {
283  {
284  new Tag( iq, "query", "xmlns", XMLNS_BYTESTREAMS );
285  e->addAttribute( "code", "403" );
286  e->addAttribute( "type", "auth" );
287  Tag *f = new Tag( e, "forbidden" );
288  f->addAttribute( "xmlns", XMLNS_XMPP_STANZAS );
289  break;
290  }
292  {
293  e->addAttribute( "code", "404" );
294  e->addAttribute( "type", "cancel" );
295  Tag *f = new Tag( e, "item-not-found" );
296  f->addAttribute( "xmlns", XMLNS_XMPP_STANZAS );
297  break;
298  }
300  {
301  new Tag( iq, "query", "xmlns", XMLNS_BYTESTREAMS );
302  e->addAttribute( "code", "405" );
303  e->addAttribute( "type", "cancel" );
304  Tag *f = new Tag( e, "not-allowed" );
305  f->addAttribute( "xmlns", XMLNS_XMPP_STANZAS );
306  break;
307  }
309  default:
310  {
311  e->addAttribute( "code", "406" );
312  e->addAttribute( "type", "auth" );
313  Tag *f = new Tag( e, "not-acceptable" );
314  f->addAttribute( "xmlns", XMLNS_XMPP_STANZAS );
315  break;
316  }
317  }
318  m_parent->send( iq );
319  }
320 
321  bool SOCKS5BytestreamManager::handleIqID( Stanza *stanza, int context )
322  {
323  StringMap::iterator it = m_trackMap.find( stanza->id() );
324  if( it == m_trackMap.end() )
325  return false;
326 
327  switch( context )
328  {
329  case S5BOpenStream:
330  {
331  switch( stanza->subtype() )
332  {
333  case StanzaIqResult:
334  {
335  Tag* q = stanza->findChild( "query", "xmlns", XMLNS_BYTESTREAMS );
336  if( !q || !m_socks5BytestreamHandler )
337  return false;
338 
339  Tag* s = q->findChild( "streamhost-used" );
340  if( !s || !s->hasAttribute( "jid" ) )
341  return false;
342 
343  const std::string & proxy = s->findAttribute( "jid" );
344  const StreamHost* sh = findProxy( stanza->from(), proxy, (*it).second );
345  if( sh )
346  {
347  SOCKS5Bytestream* s5b = 0;
348  bool selfProxy = ( proxy == m_parent->jid().full() && m_server );
349  if( selfProxy )
350  {
351  SHA sha;
352  sha.feed( (*it).second );
353  sha.feed( m_parent->jid().full() );
354  sha.feed( stanza->from().full() );
355  s5b = new SOCKS5Bytestream( this, m_server->getConnection( sha.hex() ),
356  m_parent->logInstance(),
357  m_parent->jid(), stanza->from(),
358  (*it).second );
359  }
360  else
361  {
362  s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
363  m_parent->logInstance(),
364  m_parent->jid(), stanza->from(),
365  (*it).second );
366  StreamHostList shl;
367  shl.push_back( *sh );
368  s5b->setStreamHosts( shl );
369  }
370  m_s5bMap[(*it).second] = s5b;
371  m_socks5BytestreamHandler->handleOutgoingSOCKS5Bytestream( s5b );
372  if( selfProxy )
373  s5b->activate();
374  }
375  break;
376  }
377  case StanzaIqError:
378  m_socks5BytestreamHandler->handleSOCKS5BytestreamError( stanza, (*it).second );
379  break;
380  default:
381  break;
382  }
383  break;
384  }
385  case S5BActivateStream:
386  {
387  switch( stanza->subtype() )
388  {
389  case StanzaIqResult:
390  {
391  S5BMap::const_iterator it5 = m_s5bMap.find( (*it).second );
392  if( it5 != m_s5bMap.end() )
393  (*it5).second->activate();
394  break;
395  }
396  case StanzaIqError:
397  m_socks5BytestreamHandler->handleSOCKS5BytestreamError( stanza, (*it).second );
398  break;
399  default:
400  break;
401  }
402  break;
403  }
404  default:
405  break;
406  }
407  m_trackMap.erase( it );
408 
409  return false;
410  }
411 
413  {
414  S5BMap::iterator it = m_s5bMap.find( s5b->sid() );
415  if( it != m_s5bMap.end() )
416  {
417  delete s5b;
418  m_s5bMap.erase( it );
419  return true;
420  }
421 
422  return false;
423  }
424 
425 }