1 /*
2 * s5b.cpp - direct connection protocol via tcp
3 * Copyright (C) 2003 Justin Karneges
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "s5b.h"
22
23 #include <QTimer>
24 #include <QPointer>
25 #include <QByteArray>
26 #include <stdlib.h>
27 #include <qca.h>
28
29 #include "xmpp_xmlcommon.h"
30 #include "im.h"
31 #include "socks.h"
32 #include "safedelete.h"
33
34 #ifdef Q_OS_WIN
35 # include <windows.h>
36 #else
37 # include <netinet/in.h>
38 #endif
39
40 #define MAXSTREAMHOSTS 5
41
42 static const char *S5B_NS = "http://jabber.org/protocol/bytestreams";
43
44 namespace XMPP {
45
makeKey(const QString & sid,const Jid & requester,const Jid & target)46 static QString makeKey(const QString &sid, const Jid &requester, const Jid &target)
47 {
48 #ifdef S5B_DEBUG
49 qDebug("makeKey: sid=%s requester=%s target=%s %s", qPrintable(sid),
50 qPrintable(requester.full()), qPrintable(target.full()),
51 qPrintable(QCA::Hash("sha1").hashToString(QString(sid + requester.full() + target.full()).toUtf8())));
52 #endif
53 QString str = sid + requester.full() + target.full();
54 return QCA::Hash("sha1").hashToString(str.toUtf8());
55 }
56
haveHost(const StreamHostList & list,const Jid & j)57 static bool haveHost(const StreamHostList &list, const Jid &j)
58 {
59 for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
60 if((*it).jid().compare(j))
61 return true;
62 }
63 return false;
64 }
65
66 class S5BManager::Item : public QObject
67 {
68 Q_OBJECT
69 public:
70 enum { Idle, Requester, Target, Active };
71 enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy };
72 enum { Unknown, Fast, NotFast };
73 S5BManager *m;
74 int state;
75 QString sid, key, out_key, out_id, in_id;
76 Jid self, peer;
77 StreamHostList in_hosts;
78 JT_S5B *task, *proxy_task;
79 SocksClient *client, *client_out;
80 SocksUDP *client_udp, *client_out_udp;
81 S5BConnector *conn, *proxy_conn;
82 bool wantFast;
83 StreamHost proxy;
84 int targetMode; // requester sets this once it figures it out
85 bool fast; // target sets this
86 bool activated;
87 bool lateProxy;
88 bool connSuccess;
89 bool localFailed, remoteFailed;
90 bool allowIncoming;
91 bool udp;
92 int statusCode;
93 Jid activatedStream;
94
95 Item(S5BManager *manager);
96 ~Item();
97
98 void resetConnection();
99 void startRequester(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool udp);
100 void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer,
101 const QString &_dstaddr, const StreamHostList &hosts,
102 const QString &iq_id, bool fast, bool udp);
103 void handleFast(const StreamHostList &hosts, const QString &iq_id);
104
105 void doOutgoing();
106 void doIncoming();
107 void setIncomingClient(SocksClient *sc);
108 void incomingActivate(const Jid &streamHost);
109
110 signals:
111 void accepted();
112 void tryingHosts(const StreamHostList &list);
113 void proxyConnect();
114 void waitingForActivation();
115 void connected();
116 void error(int);
117
118 private slots:
119 void jt_finished();
120 void conn_result(bool b);
121 void proxy_result(bool b);
122 void proxy_finished();
123 void sc_readyRead();
124 void sc_bytesWritten(qint64);
125 void sc_error(int);
126
127 private:
128 void doConnectError();
129 void tryActivation();
130 void checkForActivation();
131 void checkFailure();
132 void finished();
133 };
134
135 //----------------------------------------------------------------------------
136 // S5BDatagram
137 //----------------------------------------------------------------------------
S5BDatagram()138 S5BDatagram::S5BDatagram()
139 {
140 _source = 0;
141 _dest = 0;
142 }
143
S5BDatagram(int source,int dest,const QByteArray & data)144 S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data)
145 {
146 _source = source;
147 _dest = dest;
148 _buf = data;
149 }
150
sourcePort() const151 int S5BDatagram::sourcePort() const
152 {
153 return _source;
154 }
155
destPort() const156 int S5BDatagram::destPort() const
157 {
158 return _dest;
159 }
160
data() const161 QByteArray S5BDatagram::data() const
162 {
163 return _buf;
164 }
165
166 //----------------------------------------------------------------------------
167 // S5BConnection
168 //----------------------------------------------------------------------------
169 class S5BConnection::Private
170 {
171 public:
172 S5BManager *m;
173 SocksClient *sc;
174 SocksUDP *su;
175 int state;
176 Jid peer;
177 QString sid;
178 bool remote;
179 bool switched;
180 bool notifyRead, notifyClose;
181 int id;
182 S5BRequest req;
183 Jid proxy;
184 Mode mode;
185 QList<S5BDatagram*> dglist;
186 };
187
188 static int id_conn = 0;
189 static int num_conn = 0;
190
S5BConnection(S5BManager * m,QObject * parent)191 S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
192 : BSConnection(parent)
193 {
194 d = new Private;
195 d->m = m;
196 d->sc = 0;
197 d->su = 0;
198
199 ++num_conn;
200 d->id = id_conn++;
201 #ifdef S5B_DEBUG
202 qDebug("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
203 #endif
204
205 resetConnection();
206 }
207
~S5BConnection()208 S5BConnection::~S5BConnection()
209 {
210 resetConnection(true);
211
212 --num_conn;
213 #ifdef S5B_DEBUG
214 qDebug("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
215 #endif
216
217 delete d;
218 }
219
resetConnection(bool clear)220 void S5BConnection::resetConnection(bool clear)
221 {
222 d->m->con_unlink(this);
223 if(clear && d->sc) {
224 delete d->sc;
225 d->sc = 0;
226 }
227 delete d->su;
228 d->su = 0;
229 if(clear) {
230 while (!d->dglist.isEmpty()) {
231 delete d->dglist.takeFirst();
232 }
233 }
234 d->state = Idle;
235 setOpenMode(QIODevice::NotOpen);
236 d->peer = Jid();
237 d->sid = QString();
238 d->remote = false;
239 d->switched = false;
240 d->notifyRead = false;
241 d->notifyClose = false;
242 }
243
proxy() const244 Jid S5BConnection::proxy() const
245 {
246 return d->proxy;
247 }
248
setProxy(const Jid & proxy)249 void S5BConnection::setProxy(const Jid &proxy)
250 {
251 d->proxy = proxy;
252 }
253
connectToJid(const Jid & peer,const QString & sid,Mode m)254 void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m)
255 {
256 resetConnection(true);
257 if(!d->m->isAcceptableSID(peer, sid))
258 return;
259
260 d->peer = peer;
261 d->sid = sid;
262 d->state = Requesting;
263 d->mode = m;
264 #ifdef S5B_DEBUG
265 qDebug("S5BConnection[%d]: connecting %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
266 #endif
267 d->m->con_connect(this);
268 }
269
accept()270 void S5BConnection::accept()
271 {
272 if(d->state != WaitingForAccept)
273 return;
274
275 d->state = Connecting;
276 #ifdef S5B_DEBUG
277 qDebug("S5BConnection[%d]: accepting %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
278 #endif
279 d->m->con_accept(this);
280 }
281
close()282 void S5BConnection::close()
283 {
284 if(d->state == Idle)
285 return;
286
287 if(d->state == WaitingForAccept)
288 d->m->con_reject(this);
289 else if(d->state == Active)
290 d->sc->close();
291 #ifdef S5B_DEBUG
292 qDebug("S5BConnection[%d]: closing %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
293 #endif
294 resetConnection();
295 }
296
peer() const297 Jid S5BConnection::peer() const
298 {
299 return d->peer;
300 }
301
sid() const302 QString S5BConnection::sid() const
303 {
304 return d->sid;
305 }
306
manager() const307 BytestreamManager* S5BConnection::manager() const
308 {
309 return d->m;
310 }
311
isRemote() const312 bool S5BConnection::isRemote() const
313 {
314 return d->remote;
315 }
316
mode() const317 S5BConnection::Mode S5BConnection::mode() const
318 {
319 return d->mode;
320 }
321
state() const322 int S5BConnection::state() const
323 {
324 return d->state;
325 }
326
writeData(const char * data,qint64 maxSize)327 qint64 S5BConnection::writeData(const char *data, qint64 maxSize)
328 {
329 if(d->state == Active && d->mode == Stream)
330 return d->sc->write(data, maxSize);
331 return 0;
332 }
333
readData(char * data,qint64 maxSize)334 qint64 S5BConnection::readData(char *data, qint64 maxSize)
335 {
336 if(d->sc)
337 return d->sc->read(data, maxSize);
338 else
339 return 0;
340 }
341
bytesAvailable() const342 qint64 S5BConnection::bytesAvailable() const
343 {
344 if(d->sc)
345 return d->sc->bytesAvailable();
346 else
347 return 0;
348 }
349
bytesToWrite() const350 qint64 S5BConnection::bytesToWrite() const
351 {
352 if(d->state == Active)
353 return d->sc->bytesToWrite();
354 else
355 return 0;
356 }
357
writeDatagram(const S5BDatagram & i)358 void S5BConnection::writeDatagram(const S5BDatagram &i)
359 {
360 QByteArray buf;
361 buf.resize(i.data().size() + 4);
362 ushort ssp = htons(i.sourcePort());
363 ushort sdp = htons(i.destPort());
364 QByteArray data = i.data();
365 memcpy(buf.data(), &ssp, 2);
366 memcpy(buf.data() + 2, &sdp, 2);
367 memcpy(buf.data() + 4, data.data(), data.size());
368 sendUDP(buf);
369 }
370
readDatagram()371 S5BDatagram S5BConnection::readDatagram()
372 {
373 if(d->dglist.isEmpty())
374 return S5BDatagram();
375 S5BDatagram *i = d->dglist.takeFirst();
376 S5BDatagram val = *i;
377 delete i;
378 return val;
379 }
380
datagramsAvailable() const381 int S5BConnection::datagramsAvailable() const
382 {
383 return d->dglist.count();
384 }
385
man_waitForAccept(const S5BRequest & r)386 void S5BConnection::man_waitForAccept(const S5BRequest &r)
387 {
388 d->state = WaitingForAccept;
389 d->remote = true;
390 d->req = r;
391 d->peer = r.from;
392 d->sid = r.sid;
393 d->mode = r.udp ? Datagram : Stream;
394 }
395
man_clientReady(SocksClient * sc,SocksUDP * sc_udp)396 void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp)
397 {
398 d->sc = sc;
399 connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
400 connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
401 connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
402 connect(d->sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
403 connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
404
405 if(sc_udp) {
406 d->su = sc_udp;
407 connect(d->su, SIGNAL(packetReady(QByteArray)), SLOT(su_packetReady(QByteArray)));
408 }
409
410 d->state = Active;
411 setOpenMode(QIODevice::ReadWrite);
412 #ifdef S5B_DEBUG
413 qDebug("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
414 #endif
415
416 // bytes already in the stream?
417 if(d->sc->bytesAvailable()) {
418 #ifdef S5B_DEBUG
419 qDebug("Stream has %d bytes in it.\n", (int)d->sc->bytesAvailable());
420 #endif
421 d->notifyRead = true;
422 }
423 // closed before it got here?
424 if(!d->sc->isOpen()) {
425 #ifdef S5B_DEBUG
426 qDebug("Stream was closed before S5B request finished?\n");
427 #endif
428 d->notifyClose = true;
429 }
430 if(d->notifyRead || d->notifyClose)
431 QTimer::singleShot(0, this, SLOT(doPending()));
432 emit connected();
433 }
434
doPending()435 void S5BConnection::doPending()
436 {
437 if(d->notifyRead) {
438 if(d->notifyClose)
439 QTimer::singleShot(0, this, SLOT(doPending()));
440 sc_readyRead();
441 }
442 else if(d->notifyClose)
443 sc_connectionClosed();
444 }
445
man_udpReady(const QByteArray & buf)446 void S5BConnection::man_udpReady(const QByteArray &buf)
447 {
448 handleUDP(buf);
449 }
450
man_failed(int x)451 void S5BConnection::man_failed(int x)
452 {
453 resetConnection(true);
454 if(x == S5BManager::Item::ErrRefused)
455 setError(ErrRefused);
456 if(x == S5BManager::Item::ErrConnect)
457 setError(ErrConnect);
458 if(x == S5BManager::Item::ErrWrongHost)
459 setError(ErrConnect);
460 if(x == S5BManager::Item::ErrProxy)
461 setError(ErrProxy);
462 }
463
sc_connectionClosed()464 void S5BConnection::sc_connectionClosed()
465 {
466 // if we have a pending read notification, postpone close
467 if(d->notifyRead) {
468 #ifdef S5B_DEBUG
469 qDebug("closed while pending read\n");
470 #endif
471 d->notifyClose = true;
472 return;
473 }
474 d->notifyClose = false;
475 resetConnection();
476 connectionClosed();
477 }
478
sc_delayedCloseFinished()479 void S5BConnection::sc_delayedCloseFinished()
480 {
481 // echo
482 emit delayedCloseFinished();
483 }
484
sc_readyRead()485 void S5BConnection::sc_readyRead()
486 {
487 if(d->mode == Datagram) {
488 // throw the data away
489 d->sc->readAll();
490 return;
491 }
492
493 d->notifyRead = false;
494 // echo
495 emit readyRead();
496 }
497
sc_bytesWritten(qint64 x)498 void S5BConnection::sc_bytesWritten(qint64 x)
499 {
500 // echo
501 bytesWritten(x);
502 }
503
sc_error(int)504 void S5BConnection::sc_error(int)
505 {
506 resetConnection();
507 setError(ErrSocket);
508 }
509
su_packetReady(const QByteArray & buf)510 void S5BConnection::su_packetReady(const QByteArray &buf)
511 {
512 handleUDP(buf);
513 }
514
handleUDP(const QByteArray & buf)515 void S5BConnection::handleUDP(const QByteArray &buf)
516 {
517 // must be at least 4 bytes, to accomodate virtual ports
518 if(buf.size() < 4)
519 return; // drop
520
521 ushort ssp, sdp;
522 memcpy(&ssp, buf.data(), 2);
523 memcpy(&sdp, buf.data() + 2, 2);
524 int source = ntohs(ssp);
525 int dest = ntohs(sdp);
526 QByteArray data;
527 data.resize(buf.size() - 4);
528 memcpy(data.data(), buf.data() + 4, data.size());
529 d->dglist.append(new S5BDatagram(source, dest, data));
530
531 datagramReady();
532 }
533
sendUDP(const QByteArray & buf)534 void S5BConnection::sendUDP(const QByteArray &buf)
535 {
536 if(d->su)
537 d->su->write(buf);
538 else
539 d->m->con_sendUDP(this, buf);
540 }
541
542 //----------------------------------------------------------------------------
543 // S5BManager
544 //----------------------------------------------------------------------------
545 class S5BManager::Entry
546 {
547 public:
Entry()548 Entry()
549 {
550 i = 0;
551 query = 0;
552 udp_init = false;
553 }
554
~Entry()555 ~Entry()
556 {
557 delete query;
558 }
559
560 S5BConnection *c;
561 Item *i;
562 QString sid;
563 JT_S5B *query;
564 StreamHost proxyInfo;
565 QPointer<S5BServer> relatedServer;
566
567 bool udp_init;
568 QHostAddress udp_addr;
569 int udp_port;
570 };
571
572 class S5BManager::Private
573 {
574 public:
575 Client *client;
576 S5BServer *serv;
577 QList<Entry*> activeList;
578 S5BConnectionList incomingConns;
579 JT_PushS5B *ps;
580 };
581
S5BManager(Client * parent)582 S5BManager::S5BManager(Client *parent)
583 : BytestreamManager(parent)
584 {
585 // S5B needs SHA1
586 //if(!QCA::isSupported(QCA::CAP_SHA1))
587 // QCA::insertProvider(createProviderHash());
588
589 d = new Private;
590 d->client = parent;
591 d->serv = 0;
592
593 d->ps = new JT_PushS5B(d->client->rootTask());
594 connect(d->ps, SIGNAL(incoming(S5BRequest)), SLOT(ps_incoming(S5BRequest)));
595 connect(d->ps, SIGNAL(incomingUDPSuccess(Jid,QString)), SLOT(ps_incomingUDPSuccess(Jid,QString)));
596 connect(d->ps, SIGNAL(incomingActivate(Jid,QString,Jid)), SLOT(ps_incomingActivate(Jid,QString,Jid)));
597 }
598
~S5BManager()599 S5BManager::~S5BManager()
600 {
601 setServer(0);
602 while (!d->incomingConns.isEmpty()) {
603 delete d->incomingConns.takeFirst();
604 }
605 delete d->ps;
606 delete d;
607 }
608
ns()609 const char* S5BManager::ns()
610 {
611 return S5B_NS;
612 }
613
client() const614 Client *S5BManager::client() const
615 {
616 return d->client;
617 }
618
server() const619 S5BServer *S5BManager::server() const
620 {
621 return d->serv;
622 }
623
setServer(S5BServer * serv)624 void S5BManager::setServer(S5BServer *serv)
625 {
626 if(d->serv) {
627 d->serv->unlink(this);
628 d->serv = 0;
629 }
630
631 if(serv) {
632 d->serv = serv;
633 d->serv->link(this);
634 }
635 }
636
createConnection()637 BSConnection *S5BManager::createConnection()
638 {
639 return new S5BConnection(this);
640 }
641
takeIncoming()642 S5BConnection *S5BManager::takeIncoming()
643 {
644 if(d->incomingConns.isEmpty())
645 return 0;
646
647 S5BConnection *c = d->incomingConns.takeFirst();
648
649 // move to activeList
650 Entry *e = new Entry;
651 e->c = c;
652 e->sid = c->d->sid;
653 d->activeList.append(e);
654
655 return c;
656 }
657
ps_incoming(const S5BRequest & req)658 void S5BManager::ps_incoming(const S5BRequest &req)
659 {
660 #ifdef S5B_DEBUG
661 qDebug("S5BManager: incoming from %s\n", qPrintable(req.from.full()));
662 #endif
663
664 bool ok = false;
665 // ensure we don't already have an incoming connection from this peer+sid
666 S5BConnection *c = findIncoming(req.from, req.sid);
667 if(!c) {
668 // do we have an active entry with this sid already?
669 Entry *e = findEntryBySID(req.from, req.sid);
670 if(e) {
671 if(e->i) {
672 // loopback
673 if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) {
674 #ifdef S5B_DEBUG
675 qDebug("ALLOWED: loopback\n");
676 #endif
677 ok = true;
678 }
679 // allowed by 'fast mode'
680 else if(e->i->state == Item::Requester && e->i->targetMode == Item::Unknown) {
681 #ifdef S5B_DEBUG
682 qDebug("ALLOWED: fast-mode\n");
683 #endif
684 e->i->handleFast(req.hosts, req.id);
685 return;
686 }
687 }
688 }
689 else {
690 #ifdef S5B_DEBUG
691 qDebug("ALLOWED: we don't have it\n");
692 #endif
693 ok = true;
694 }
695 }
696 if(!ok) {
697 d->ps->respondError(req.from, req.id, Stanza::Error::NotAcceptable, "SID in use");
698 return;
699 }
700
701 // create an incoming connection
702 c = new S5BConnection(this);
703 c->man_waitForAccept(req);
704 d->incomingConns.append(c);
705 emit incomingReady();
706 }
707
ps_incomingUDPSuccess(const Jid & from,const QString & key)708 void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key)
709 {
710 Entry *e = findEntryByHash(key);
711 if(e && e->i) {
712 if(e->i->conn)
713 e->i->conn->man_udpSuccess(from);
714 else if(e->i->proxy_conn)
715 e->i->proxy_conn->man_udpSuccess(from);
716 }
717 }
718
ps_incomingActivate(const Jid & from,const QString & sid,const Jid & streamHost)719 void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost)
720 {
721 Entry *e = findEntryBySID(from, sid);
722 if(e && e->i)
723 e->i->incomingActivate(streamHost);
724 }
725
doSuccess(const Jid & peer,const QString & id,const Jid & streamHost)726 void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
727 {
728 d->ps->respondSuccess(peer, id, streamHost);
729 }
730
doError(const Jid & peer,const QString & id,Stanza::Error::ErrorCond cond,const QString & str)731 void S5BManager::doError(const Jid &peer, const QString &id,
732 Stanza::Error::ErrorCond cond, const QString &str)
733 {
734 d->ps->respondError(peer, id, cond, str);
735 }
736
doActivate(const Jid & peer,const QString & sid,const Jid & streamHost)737 void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &streamHost)
738 {
739 d->ps->sendActivate(peer, sid, streamHost);
740 }
741
isAcceptableSID(const Jid & peer,const QString & sid) const742 bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
743 {
744 QString key = makeKey(sid, d->client->jid(), peer);
745 QString key_out = makeKey(sid, peer, d->client->jid()); //not valid in muc via proxy
746
747 // if we have a server, then check through it
748 if(d->serv) {
749 if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
750 return false;
751 }
752 else {
753 if(findEntryByHash(key) || findEntryByHash(key_out))
754 return false;
755 }
756 return true;
757 }
758
sidPrefix() const759 const char* S5BManager::sidPrefix() const
760 {
761 return "s5b_";
762 }
763
findIncoming(const Jid & from,const QString & sid) const764 S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
765 {
766 foreach(S5BConnection *c, d->incomingConns) {
767 if(c->d->peer.compare(from) && c->d->sid == sid)
768 return c;
769 }
770 return 0;
771 }
772
findEntry(S5BConnection * c) const773 S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
774 {
775 foreach(Entry *e, d->activeList) {
776 if(e->c == c)
777 return e;
778 }
779 return 0;
780 }
781
findEntry(Item * i) const782 S5BManager::Entry *S5BManager::findEntry(Item *i) const
783 {
784 foreach(Entry *e, d->activeList) {
785 if(e->i == i)
786 return e;
787 }
788 return 0;
789 }
790
findEntryByHash(const QString & key) const791 S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
792 {
793 foreach(Entry *e, d->activeList) {
794 if(e->i && e->i->key == key)
795 return e;
796 }
797 return 0;
798 }
799
findEntryBySID(const Jid & peer,const QString & sid) const800 S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
801 {
802 foreach(Entry *e, d->activeList) {
803 if(e->i && e->i->peer.compare(peer) && e->sid == sid)
804 return e;
805 }
806 return 0;
807 }
808
findServerEntryByHash(const QString & key) const809 S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
810 {
811 const QList<S5BManager*> &manList = d->serv->managerList();
812 foreach(S5BManager *m, manList) {
813 Entry *e = m->findEntryByHash(key);
814 if(e)
815 return e;
816 }
817 return 0;
818 }
819
srv_ownsHash(const QString & key) const820 bool S5BManager::srv_ownsHash(const QString &key) const
821 {
822 if(findEntryByHash(key))
823 return true;
824 return false;
825 }
826
srv_incomingReady(SocksClient * sc,const QString & key)827 void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
828 {
829 Entry *e = findEntryByHash(key);
830 if(!e->i->allowIncoming) {
831 sc->requestDeny();
832 sc->deleteLater();
833 return;
834 }
835 if(e->c->d->mode == S5BConnection::Datagram)
836 sc->grantUDPAssociate("", 0);
837 else
838 sc->grantConnect();
839 e->relatedServer = (S5BServer *)sender();
840 e->i->setIncomingClient(sc);
841 }
842
srv_incomingUDP(bool init,const QHostAddress & addr,int port,const QString & key,const QByteArray & data)843 void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data)
844 {
845 Entry *e = findEntryByHash(key);
846 if(e->c->d->mode != S5BConnection::Datagram)
847 return; // this key isn't in udp mode? drop!
848
849 if(init) {
850 if(e->udp_init)
851 return; // only init once
852
853 // lock on to this sender
854 e->udp_addr = addr;
855 e->udp_port = port;
856 e->udp_init = true;
857
858 // reply that initialization was successful
859 d->ps->sendUDPSuccess(e->c->d->peer, key);
860 return;
861 }
862
863 // not initialized yet? something went wrong
864 if(!e->udp_init)
865 return;
866
867 // must come from same source as when initialized
868 if(addr.toString() != e->udp_addr.toString() || port != e->udp_port)
869 return;
870
871 e->c->man_udpReady(data);
872 }
873
srv_unlink()874 void S5BManager::srv_unlink()
875 {
876 d->serv = 0;
877 }
878
con_connect(S5BConnection * c)879 void S5BManager::con_connect(S5BConnection *c)
880 {
881 if(findEntry(c))
882 return;
883 Entry *e = new Entry;
884 e->c = c;
885 e->sid = c->d->sid;
886 d->activeList.append(e);
887
888 if(c->d->proxy.isValid()) {
889 queryProxy(e);
890 return;
891 }
892 entryContinue(e);
893 }
894
con_accept(S5BConnection * c)895 void S5BManager::con_accept(S5BConnection *c)
896 {
897 Entry *e = findEntry(c);
898 if(!e)
899 return;
900
901 if(e->c->d->req.fast) {
902 if(targetShouldOfferProxy(e)) {
903 queryProxy(e);
904 return;
905 }
906 }
907 entryContinue(e);
908 }
909
con_reject(S5BConnection * c)910 void S5BManager::con_reject(S5BConnection *c)
911 {
912 d->ps->respondError(c->d->peer, c->d->req.id, Stanza::Error::NotAcceptable,
913 "Not acceptable");
914 }
915
con_unlink(S5BConnection * c)916 void S5BManager::con_unlink(S5BConnection *c)
917 {
918 Entry *e = findEntry(c);
919 if(!e)
920 return;
921
922 // active incoming request? cancel it
923 if(e->i && e->i->conn)
924 d->ps->respondError(e->i->peer, e->i->out_id,
925 Stanza::Error::NotAcceptable, "Not acceptable");
926 delete e->i;
927 d->activeList.removeAll(e);
928 delete e;
929 }
930
con_sendUDP(S5BConnection * c,const QByteArray & buf)931 void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf)
932 {
933 Entry *e = findEntry(c);
934 if(!e)
935 return;
936 if(!e->udp_init)
937 return;
938
939 if(e->relatedServer)
940 e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf);
941 }
942
item_accepted()943 void S5BManager::item_accepted()
944 {
945 Item *i = (Item *)sender();
946 Entry *e = findEntry(i);
947
948 emit e->c->accepted(); // signal
949 }
950
item_tryingHosts(const StreamHostList & list)951 void S5BManager::item_tryingHosts(const StreamHostList &list)
952 {
953 Item *i = (Item *)sender();
954 Entry *e = findEntry(i);
955
956 e->c->tryingHosts(list); // signal
957 }
958
item_proxyConnect()959 void S5BManager::item_proxyConnect()
960 {
961 Item *i = (Item *)sender();
962 Entry *e = findEntry(i);
963
964 e->c->proxyConnect(); // signal
965 }
966
item_waitingForActivation()967 void S5BManager::item_waitingForActivation()
968 {
969 Item *i = (Item *)sender();
970 Entry *e = findEntry(i);
971
972 e->c->waitingForActivation(); // signal
973 }
974
item_connected()975 void S5BManager::item_connected()
976 {
977 Item *i = (Item *)sender();
978 Entry *e = findEntry(i);
979
980 // grab the client
981 SocksClient *client = i->client;
982 i->client = 0;
983 SocksUDP *client_udp = i->client_udp;
984 i->client_udp = 0;
985
986 // give it to the connection
987 e->c->man_clientReady(client, client_udp);
988 }
989
item_error(int x)990 void S5BManager::item_error(int x)
991 {
992 Item *i = (Item *)sender();
993 Entry *e = findEntry(i);
994
995 e->c->man_failed(x);
996 }
997
entryContinue(Entry * e)998 void S5BManager::entryContinue(Entry *e)
999 {
1000 e->i = new Item(this);
1001 e->i->proxy = e->proxyInfo;
1002
1003 connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
1004 connect(e->i, SIGNAL(tryingHosts(StreamHostList)), SLOT(item_tryingHosts(StreamHostList)));
1005 connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
1006 connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
1007 connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
1008 connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
1009
1010 if(e->c->isRemote()) {
1011 const S5BRequest &req = e->c->d->req;
1012 e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.dstaddr, req.hosts, req.id, req.fast, req.udp);
1013 }
1014 else {
1015 e->i->startRequester(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false);
1016 e->c->requesting(); // signal
1017 }
1018 }
1019
queryProxy(Entry * e)1020 void S5BManager::queryProxy(Entry *e)
1021 {
1022 QPointer<QObject> self = this;
1023 e->c->proxyQuery(); // signal
1024 if(!self)
1025 return;
1026
1027 #ifdef S5B_DEBUG
1028 qDebug("querying proxy: [%s]\n", qPrintable(e->c->d->proxy.full()));
1029 #endif
1030 e->query = new JT_S5B(d->client->rootTask());
1031 connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
1032 e->query->requestProxyInfo(e->c->d->proxy);
1033 e->query->go(true);
1034 }
1035
query_finished()1036 void S5BManager::query_finished()
1037 {
1038 JT_S5B *query = (JT_S5B *)sender();
1039 Entry* e = 0;
1040 foreach(Entry* i, d->activeList) {
1041 if(i->query == query) {
1042 e = i;
1043 break;
1044 }
1045 }
1046 if(!e)
1047 return;
1048 e->query = 0;
1049
1050 #ifdef S5B_DEBUG
1051 qDebug("query finished: ");
1052 #endif
1053 if(query->success()) {
1054 e->proxyInfo = query->proxyInfo();
1055 #ifdef S5B_DEBUG
1056 qDebug("host/ip=[%s] port=[%d]\n", qPrintable(e->proxyInfo.host()), e->proxyInfo.port());
1057 #endif
1058 }
1059 else {
1060 #ifdef S5B_DEBUG
1061 qDebug("fail\n");
1062 #endif
1063 }
1064
1065 QPointer<QObject> self = this;
1066 e->c->proxyResult(query->success()); // signal
1067 if(!self)
1068 return;
1069
1070 entryContinue(e);
1071 }
1072
targetShouldOfferProxy(Entry * e)1073 bool S5BManager::targetShouldOfferProxy(Entry *e)
1074 {
1075 if(!e->c->d->proxy.isValid())
1076 return false;
1077
1078 // if target, don't offer any proxy if the requester already did
1079 const StreamHostList &hosts = e->c->d->req.hosts;
1080 for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1081 if((*it).isProxy())
1082 return false;
1083 }
1084
1085 // ensure we don't offer the same proxy as the requester
1086 if(haveHost(hosts, e->c->d->proxy))
1087 return false;
1088
1089 return true;
1090 }
1091
1092 //----------------------------------------------------------------------------
1093 // S5BManager::Item
1094 //----------------------------------------------------------------------------
Item(S5BManager * manager)1095 S5BManager::Item::Item(S5BManager *manager) : QObject(0)
1096 {
1097 m = manager;
1098 task = 0;
1099 proxy_task = 0;
1100 conn = 0;
1101 proxy_conn = 0;
1102 client_udp = 0;
1103 client = 0;
1104 client_out_udp = 0;
1105 client_out = 0;
1106 resetConnection();
1107 }
1108
~Item()1109 S5BManager::Item::~Item()
1110 {
1111 resetConnection();
1112 }
1113
resetConnection()1114 void S5BManager::Item::resetConnection()
1115 {
1116 delete task;
1117 task = 0;
1118
1119 delete proxy_task;
1120 proxy_task = 0;
1121
1122 delete conn;
1123 conn = 0;
1124
1125 delete proxy_conn;
1126 proxy_conn = 0;
1127
1128 delete client_udp;
1129 client_udp = 0;
1130
1131 delete client;
1132 client = 0;
1133
1134 delete client_out_udp;
1135 client_out_udp = 0;
1136
1137 delete client_out;
1138 client_out = 0;
1139
1140 state = Idle;
1141 wantFast = false;
1142 targetMode = Unknown;
1143 fast = false;
1144 activated = false;
1145 lateProxy = false;
1146 connSuccess = false;
1147 localFailed = false;
1148 remoteFailed = false;
1149 allowIncoming = false;
1150 udp = false;
1151 }
1152
startRequester(const QString & _sid,const Jid & _self,const Jid & _peer,bool fast,bool _udp)1153 void S5BManager::Item::startRequester(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp)
1154 {
1155 sid = _sid;
1156 self = _self;
1157 peer = _peer;
1158 key = makeKey(sid, self, peer);
1159 out_key = makeKey(sid, peer, self);
1160 wantFast = fast;
1161 udp = _udp;
1162
1163 #ifdef S5B_DEBUG
1164 qDebug("S5BManager::Item initiating request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key));
1165 #endif
1166 state = Requester;
1167 doOutgoing();
1168 }
1169
startTarget(const QString & _sid,const Jid & _self,const Jid & _peer,const QString & _dstaddr,const StreamHostList & hosts,const QString & iq_id,bool _fast,bool _udp)1170 void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self,
1171 const Jid &_peer, const QString &_dstaddr,
1172 const StreamHostList &hosts, const QString &iq_id,
1173 bool _fast, bool _udp)
1174 {
1175 sid = _sid;
1176 peer = _peer;
1177 self = _self;
1178 in_hosts = hosts;
1179 in_id = iq_id;
1180 fast = _fast;
1181 key = makeKey(sid, self, peer);
1182 out_key = _dstaddr.isEmpty() ? makeKey(sid, peer, self) : _dstaddr;
1183 udp = _udp;
1184
1185 #ifdef S5B_DEBUG
1186 qDebug("S5BManager::Item incoming request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key));
1187 #endif
1188 state = Target;
1189 if(fast)
1190 doOutgoing();
1191 doIncoming();
1192 }
1193
handleFast(const StreamHostList & hosts,const QString & iq_id)1194 void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
1195 {
1196 targetMode = Fast;
1197
1198 QPointer<QObject> self = this;
1199 emit accepted();
1200 if(!self)
1201 return;
1202
1203 // if we already have a stream, then bounce this request
1204 if(client) {
1205 m->doError(peer, iq_id, Stanza::Error::NotAcceptable, "Not acceptable");
1206 }
1207 else {
1208 in_hosts = hosts;
1209 in_id = iq_id;
1210 doIncoming();
1211 }
1212 }
1213
doOutgoing()1214 void S5BManager::Item::doOutgoing()
1215 {
1216 StreamHostList hosts;
1217 S5BServer *serv = m->server();
1218 if(serv && serv->isActive() && !haveHost(in_hosts, self)) {
1219 QStringList hostList = serv->hostList();
1220 foreach (const QString & it, hostList) {
1221 StreamHost h;
1222 h.setJid(self);
1223 h.setHost(it);
1224 h.setPort(serv->port());
1225 hosts += h;
1226 }
1227 }
1228
1229 // if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
1230 if(proxy.jid().isValid())
1231 hosts += proxy;
1232
1233 // if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
1234 if(state == Target && hosts.isEmpty()) {
1235 fast = false;
1236 return;
1237 }
1238
1239 allowIncoming = true;
1240
1241 task = new JT_S5B(m->client()->rootTask());
1242 connect(task, SIGNAL(finished()), SLOT(jt_finished()));
1243 task->request(peer, sid, key, hosts, state == Requester ? wantFast : false, udp);
1244 out_id = task->id();
1245 task->go(true);
1246 }
1247
doIncoming()1248 void S5BManager::Item::doIncoming()
1249 {
1250 if(in_hosts.isEmpty()) {
1251 doConnectError();
1252 return;
1253 }
1254
1255 StreamHostList list;
1256 if(lateProxy) {
1257 // take just the proxy streamhosts
1258 foreach (const StreamHost& it, in_hosts) {
1259 if (it.isProxy())
1260 list += it;
1261 }
1262 lateProxy = false;
1263 }
1264 else {
1265 // only try doing the late proxy trick if using fast mode AND we did not offer a proxy
1266 if((state == Requester || (state == Target && fast)) && !proxy.jid().isValid()) {
1267 // take just the non-proxy streamhosts
1268 bool hasProxies = false;
1269 foreach (const StreamHost& it, in_hosts) {
1270 if (it.isProxy())
1271 hasProxies = true;
1272 else
1273 list += it;
1274 }
1275 if(hasProxies) {
1276 lateProxy = true;
1277
1278 // no regular streamhosts? wait for remote error
1279 if(list.isEmpty())
1280 return;
1281 }
1282 }
1283 else
1284 list = in_hosts;
1285 }
1286
1287 conn = new S5BConnector;
1288 connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
1289
1290 QPointer<QObject> self = this;
1291 tryingHosts(list);
1292 if(!self)
1293 return;
1294
1295 conn->start(this->self, list, out_key, udp, lateProxy ? 10 : 30);
1296 }
1297
setIncomingClient(SocksClient * sc)1298 void S5BManager::Item::setIncomingClient(SocksClient *sc)
1299 {
1300 #ifdef S5B_DEBUG
1301 qDebug("S5BManager::Item: %s [%s] successful incoming connection\n", qPrintable(peer.full()), qPrintable(sid));
1302 #endif
1303
1304 connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1305 connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
1306 connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1307
1308 client = sc;
1309 allowIncoming = false;
1310 }
1311
incomingActivate(const Jid & streamHost)1312 void S5BManager::Item::incomingActivate(const Jid &streamHost)
1313 {
1314 if(!activated) {
1315 activatedStream = streamHost;
1316 checkForActivation();
1317 }
1318 }
1319
jt_finished()1320 void S5BManager::Item::jt_finished()
1321 {
1322 JT_S5B *j = task;
1323 task = 0;
1324
1325 #ifdef S5B_DEBUG
1326 qDebug("jt_finished: state=%s, %s\n", state == Requester ? "requester" : "target", j->success() ? "ok" : "fail");
1327 #endif
1328
1329 if(state == Requester) {
1330 if(targetMode == Unknown) {
1331 targetMode = NotFast;
1332 QPointer<QObject> self = this;
1333 emit accepted();
1334 if(!self)
1335 return;
1336 }
1337 }
1338
1339 // if we've already reported successfully connecting to them, then this response doesn't matter
1340 if(state == Requester && connSuccess) {
1341 tryActivation();
1342 return;
1343 }
1344
1345 if(j->success()) {
1346 // stop connecting out
1347 if(conn || lateProxy) {
1348 delete conn;
1349 conn = 0;
1350 doConnectError();
1351 }
1352
1353 Jid streamHost = j->streamHostUsed();
1354
1355 // they connected to us?
1356 if(streamHost.compare(self)) {
1357 if(client) {
1358 if(state == Requester) {
1359 activatedStream = streamHost;
1360 tryActivation();
1361 }
1362 else
1363 checkForActivation();
1364 }
1365 else {
1366 #ifdef S5B_DEBUG
1367 qDebug("S5BManager::Item %s claims to have connected to us, but we don't see this\n", qPrintable(peer.full()));
1368 #endif
1369 resetConnection();
1370 error(ErrWrongHost);
1371 }
1372 }
1373 else if(streamHost.compare(proxy.jid())) {
1374 // toss out any direct incoming, since it won't be used
1375 delete client;
1376 client = 0;
1377 allowIncoming = false;
1378
1379 #ifdef S5B_DEBUG
1380 qDebug("attempting to connect to proxy\n");
1381 #endif
1382 // connect to the proxy
1383 proxy_conn = new S5BConnector;
1384 connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
1385 StreamHostList list;
1386 list += proxy;
1387
1388 QPointer<QObject> self = this;
1389 proxyConnect();
1390 if(!self)
1391 return;
1392
1393 proxy_conn->start(this->self, list, key, udp, 30);
1394 }
1395 else {
1396 #ifdef S5B_DEBUG
1397 qDebug("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", qPrintable(peer.full()));
1398 #endif
1399 resetConnection();
1400 error(ErrWrongHost);
1401 }
1402 }
1403 else {
1404 #ifdef S5B_DEBUG
1405 qDebug("S5BManager::Item %s [%s] error\n", qPrintable(peer.full()), qPrintable(sid));
1406 #endif
1407 remoteFailed = true;
1408 statusCode = j->statusCode();
1409
1410 if(lateProxy) {
1411 if(!conn)
1412 doIncoming();
1413 }
1414 else {
1415 // if connSuccess is true at this point, then we're a Target
1416 if(connSuccess)
1417 checkForActivation();
1418 else
1419 checkFailure();
1420 }
1421 }
1422 }
1423
conn_result(bool b)1424 void S5BManager::Item::conn_result(bool b)
1425 {
1426 if(b) {
1427 SocksClient *sc = conn->takeClient();
1428 SocksUDP *sc_udp = conn->takeUDP();
1429 StreamHost h = conn->streamHostUsed();
1430 delete conn;
1431 conn = 0;
1432 connSuccess = true;
1433
1434 #ifdef S5B_DEBUG
1435 qDebug("S5BManager::Item: %s [%s] successful outgoing connection\n",
1436 qPrintable(peer.full()), qPrintable(sid));
1437 #endif
1438
1439 connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1440 connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
1441 connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1442
1443 m->doSuccess(peer, in_id, h.jid());
1444
1445 // if the first batch works, don't try proxy
1446 lateProxy = false;
1447
1448 // if requester, run with this one
1449 if(state == Requester) {
1450 // if we had an incoming one, toss it
1451 delete client_udp;
1452 client_udp = sc_udp;
1453 delete client;
1454 client = sc;
1455 allowIncoming = false;
1456 activatedStream = peer;
1457 tryActivation();
1458 }
1459 else {
1460 client_out_udp = sc_udp;
1461 client_out = sc;
1462 checkForActivation();
1463 }
1464 }
1465 else {
1466 delete conn;
1467 conn = 0;
1468
1469 // if we delayed the proxies for later, try now
1470 if(lateProxy) {
1471 if(remoteFailed)
1472 doIncoming();
1473 }
1474 else
1475 doConnectError();
1476 }
1477 }
1478
proxy_result(bool b)1479 void S5BManager::Item::proxy_result(bool b)
1480 {
1481 #ifdef S5B_DEBUG
1482 qDebug("proxy_result: %s\n", b ? "ok" : "fail");
1483 #endif
1484 if(b) {
1485 SocksClient *sc = proxy_conn->takeClient();
1486 SocksUDP *sc_udp = proxy_conn->takeUDP();
1487 delete proxy_conn;
1488 proxy_conn = 0;
1489
1490 connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1491 connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64)));
1492 connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1493
1494 client = sc;
1495 client_udp = sc_udp;
1496
1497 // activate
1498 #ifdef S5B_DEBUG
1499 qDebug("activating proxy stream\n");
1500 #endif
1501 proxy_task = new JT_S5B(m->client()->rootTask());
1502 connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
1503 proxy_task->requestActivation(proxy.jid(), sid, peer);
1504 proxy_task->go(true);
1505 }
1506 else {
1507 delete proxy_conn;
1508 proxy_conn = 0;
1509 resetConnection();
1510 error(ErrProxy);
1511 }
1512 }
1513
proxy_finished()1514 void S5BManager::Item::proxy_finished()
1515 {
1516 JT_S5B *j = proxy_task;
1517 proxy_task = 0;
1518
1519 if(j->success()) {
1520 #ifdef S5B_DEBUG
1521 qDebug("proxy stream activated\n");
1522 #endif
1523 if(state == Requester) {
1524 activatedStream = proxy.jid();
1525 tryActivation();
1526 }
1527 else
1528 checkForActivation();
1529 }
1530 else {
1531 resetConnection();
1532 error(ErrProxy);
1533 }
1534 }
1535
sc_readyRead()1536 void S5BManager::Item::sc_readyRead()
1537 {
1538 #ifdef S5B_DEBUG
1539 qDebug("sc_readyRead\n");
1540 #endif
1541 // only targets check for activation, and only should do it if there is no pending outgoing iq-set
1542 if(state == Target && !task && !proxy_task)
1543 checkForActivation();
1544 }
1545
sc_bytesWritten(qint64)1546 void S5BManager::Item::sc_bytesWritten(qint64)
1547 {
1548 #ifdef S5B_DEBUG
1549 qDebug("sc_bytesWritten\n");
1550 #endif
1551 // this should only happen to the requester, and should always be 1 byte (the '\r' sent earlier)
1552 finished();
1553 }
1554
sc_error(int)1555 void S5BManager::Item::sc_error(int)
1556 {
1557 #ifdef S5B_DEBUG
1558 qDebug("sc_error\n");
1559 #endif
1560 resetConnection();
1561 error(ErrConnect);
1562 }
1563
doConnectError()1564 void S5BManager::Item::doConnectError()
1565 {
1566 localFailed = true;
1567 m->doError(peer, in_id, Stanza::Error::RemoteServerNotFound,
1568 "Could not connect to given hosts");
1569 checkFailure();
1570 }
1571
tryActivation()1572 void S5BManager::Item::tryActivation()
1573 {
1574 #ifdef S5B_DEBUG
1575 qDebug("tryActivation\n");
1576 #endif
1577 if(activated) {
1578 #ifdef S5B_DEBUG
1579 qDebug("already activated !?\n");
1580 #endif
1581 return;
1582 }
1583
1584 if(targetMode == NotFast) {
1585 #ifdef S5B_DEBUG
1586 qDebug("tryActivation: NotFast\n");
1587 #endif
1588 // nothing to activate, we're done
1589 finished();
1590 }
1591 else if(targetMode == Fast) {
1592 // with fast mode, we don't wait for the iq reply, so delete the task (if any)
1593 delete task;
1594 task = 0;
1595
1596 activated = true;
1597
1598 // if udp, activate using special stanza
1599 if(udp) {
1600 m->doActivate(peer, sid, activatedStream);
1601 }
1602 else {
1603 #ifdef S5B_DEBUG
1604 qDebug("sending extra CR\n");
1605 #endif
1606 // must send [CR] to activate target streamhost
1607 client->write("\r", 1);
1608 }
1609 }
1610 }
1611
checkForActivation()1612 void S5BManager::Item::checkForActivation()
1613 {
1614 QList<SocksClient*> clientList;
1615 if(client)
1616 clientList.append(client);
1617 if(client_out)
1618 clientList.append(client_out);
1619 foreach(SocksClient *sc, clientList) {
1620 #ifdef S5B_DEBUG
1621 qDebug("checking for activation\n");
1622 #endif
1623 if(fast) {
1624 bool ok = false;
1625 if(udp) {
1626 if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) {
1627 clientList.removeAll(sc);
1628 ok = true;
1629 }
1630 }
1631 else {
1632 #ifdef S5B_DEBUG
1633 qDebug("need CR\n");
1634 #endif
1635 if(sc->bytesAvailable() >= 1) {
1636 clientList.removeAll(sc);
1637 char c;
1638 if(!sc->getChar(&c) || c != '\r') {
1639 delete sc; // FIXME breaks S5BManager::Item destructor?
1640 return;
1641 }
1642 ok = true;
1643 }
1644 }
1645
1646 if(ok) {
1647 SocksUDP *sc_udp = 0;
1648 if(sc == client) {
1649 delete client_out_udp;
1650 client_out_udp = 0;
1651 sc_udp = client_udp;
1652 }
1653 else if(sc == client_out) {
1654 delete client_udp;
1655 client_udp = 0;
1656 sc_udp = client_out_udp;
1657 }
1658
1659 sc->disconnect(this);
1660 while (!clientList.isEmpty()) {
1661 delete clientList.takeFirst();
1662 }
1663 client = sc;
1664 client_out = 0;
1665 client_udp = sc_udp;
1666 activated = true;
1667 #ifdef S5B_DEBUG
1668 qDebug("activation success\n");
1669 #endif
1670 break;
1671 }
1672 }
1673 else {
1674 #ifdef S5B_DEBUG
1675 qDebug("not fast mode, no need to wait for anything\n");
1676 #endif
1677 clientList.removeAll(sc);
1678 sc->disconnect(this);
1679 while (!clientList.isEmpty()) {
1680 delete clientList.takeFirst();
1681 }
1682 client = sc;
1683 client_out = 0;
1684 activated = true;
1685 break;
1686 }
1687 }
1688
1689 if(activated) {
1690 finished();
1691 }
1692 else {
1693 // only emit waitingForActivation if there is nothing left to do
1694 if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
1695 waitingForActivation();
1696 }
1697 }
1698
checkFailure()1699 void S5BManager::Item::checkFailure()
1700 {
1701 bool failed = false;
1702 if(state == Requester) {
1703 if(remoteFailed) {
1704 if((localFailed && targetMode == Fast) || targetMode == NotFast)
1705 failed = true;
1706 }
1707 }
1708 else {
1709 if(localFailed) {
1710 if((remoteFailed && fast) || !fast)
1711 failed = true;
1712 }
1713 }
1714
1715 if(failed) {
1716 if(state == Requester) {
1717 resetConnection();
1718 if(statusCode == 404)
1719 error(ErrConnect);
1720 else
1721 error(ErrRefused);
1722 }
1723 else {
1724 resetConnection();
1725 error(ErrConnect);
1726 }
1727 }
1728 }
1729
finished()1730 void S5BManager::Item::finished()
1731 {
1732 client->disconnect(this);
1733 state = Active;
1734 #ifdef S5B_DEBUG
1735 qDebug("S5BManager::Item %s [%s] linked successfully\n", qPrintable(peer.full()), qPrintable(sid));
1736 #endif
1737 emit connected();
1738 }
1739
1740 //----------------------------------------------------------------------------
1741 // S5BConnector
1742 //----------------------------------------------------------------------------
1743 class S5BConnector::Item : public QObject
1744 {
1745 Q_OBJECT
1746 public:
1747 SocksClient *client;
1748 SocksUDP *client_udp;
1749 StreamHost host;
1750 QString key;
1751 bool udp;
1752 int udp_tries;
1753 QTimer t;
1754 Jid jid;
1755
Item(const Jid & self,const StreamHost & _host,const QString & _key,bool _udp)1756 Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : QObject(0)
1757 {
1758 jid = self;
1759 host = _host;
1760 key = _key;
1761 udp = _udp;
1762 client = new SocksClient;
1763 client_udp = 0;
1764 connect(client, SIGNAL(connected()), SLOT(sc_connected()));
1765 connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1766 connect(&t, SIGNAL(timeout()), SLOT(trySendUDP()));
1767 }
1768
~Item()1769 ~Item()
1770 {
1771 cleanup();
1772 }
1773
start()1774 void start()
1775 {
1776 client->connectToHost(host.host(), host.port(), key, 0, udp);
1777 }
1778
udpSuccess()1779 void udpSuccess()
1780 {
1781 t.stop();
1782 client_udp->change(key, 0); // flip over to the data port
1783 success();
1784 }
1785
1786 signals:
1787 void result(bool);
1788
1789 private slots:
sc_connected()1790 void sc_connected()
1791 {
1792 // if udp, need to send init packet before we are good
1793 if(udp) {
1794 // port 1 is init
1795 client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort());
1796 udp_tries = 0;
1797 t.start(5000);
1798 trySendUDP();
1799 return;
1800 }
1801
1802 success();
1803 }
1804
sc_error(int)1805 void sc_error(int)
1806 {
1807 #ifdef S5B_DEBUG
1808 qDebug("S5BConnector[%s]: error\n", qPrintable(host.host()));
1809 #endif
1810 cleanup();
1811 result(false);
1812 }
1813
trySendUDP()1814 void trySendUDP()
1815 {
1816 if(udp_tries == 5) {
1817 t.stop();
1818 cleanup();
1819 result(false);
1820 return;
1821 }
1822
1823 // send initialization with our JID
1824 QByteArray a(jid.full().toUtf8());
1825 client_udp->write(a);
1826 ++udp_tries;
1827 }
1828
1829 private:
cleanup()1830 void cleanup()
1831 {
1832 delete client_udp;
1833 client_udp = 0;
1834 delete client;
1835 client = 0;
1836 }
1837
success()1838 void success()
1839 {
1840 #ifdef S5B_DEBUG
1841 qDebug("S5BConnector[%s]: success\n", qPrintable(host.host()));
1842 #endif
1843 client->disconnect(this);
1844 result(true);
1845 }
1846 };
1847
1848 class S5BConnector::Private
1849 {
1850 public:
1851 SocksClient *active;
1852 SocksUDP *active_udp;
1853 QList<Item*> itemList;
1854 QString key;
1855 StreamHost activeHost;
1856 QTimer t;
1857 };
1858
S5BConnector(QObject * parent)1859 S5BConnector::S5BConnector(QObject *parent)
1860 :QObject(parent)
1861 {
1862 d = new Private;
1863 d->active = 0;
1864 d->active_udp = 0;
1865 connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
1866 }
1867
~S5BConnector()1868 S5BConnector::~S5BConnector()
1869 {
1870 resetConnection();
1871 delete d;
1872 }
1873
resetConnection()1874 void S5BConnector::resetConnection()
1875 {
1876 d->t.stop();
1877 delete d->active_udp;
1878 d->active_udp = 0;
1879 delete d->active;
1880 d->active = 0;
1881 while (!d->itemList.empty()) {
1882 delete d->itemList.takeFirst();
1883 }
1884 }
1885
start(const Jid & self,const StreamHostList & hosts,const QString & key,bool udp,int timeout)1886 void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout)
1887 {
1888 resetConnection();
1889
1890 #ifdef S5B_DEBUG
1891 qDebug("S5BConnector: starting [%p]!\n", this);
1892 #endif
1893 for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1894 Item *i = new Item(self, *it, key, udp);
1895 connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
1896 d->itemList.append(i);
1897 i->start();
1898 }
1899 d->t.start(timeout * 1000);
1900 }
1901
takeClient()1902 SocksClient *S5BConnector::takeClient()
1903 {
1904 SocksClient *c = d->active;
1905 d->active = 0;
1906 return c;
1907 }
1908
takeUDP()1909 SocksUDP *S5BConnector::takeUDP()
1910 {
1911 SocksUDP *c = d->active_udp;
1912 d->active_udp = 0;
1913 return c;
1914 }
1915
streamHostUsed() const1916 StreamHost S5BConnector::streamHostUsed() const
1917 {
1918 return d->activeHost;
1919 }
1920
item_result(bool b)1921 void S5BConnector::item_result(bool b)
1922 {
1923 Item *i = (Item *)sender();
1924 if(b) {
1925 d->active = i->client;
1926 i->client = 0;
1927 d->active_udp = i->client_udp;
1928 i->client_udp = 0;
1929 d->activeHost = i->host;
1930 while (!d->itemList.isEmpty()) {
1931 delete d->itemList.takeFirst();
1932 }
1933 d->t.stop();
1934 #ifdef S5B_DEBUG
1935 qDebug("S5BConnector: complete! [%p]\n", this);
1936 #endif
1937 emit result(true);
1938 }
1939 else {
1940 d->itemList.removeAll(i);
1941 delete i;
1942 if(d->itemList.isEmpty()) {
1943 d->t.stop();
1944 #ifdef S5B_DEBUG
1945 qDebug("S5BConnector: failed! [%p]\n", this);
1946 #endif
1947 emit result(false);
1948 }
1949 }
1950 }
1951
t_timeout()1952 void S5BConnector::t_timeout()
1953 {
1954 resetConnection();
1955 #ifdef S5B_DEBUG
1956 qDebug("S5BConnector: failed! (timeout)\n");
1957 #endif
1958 result(false);
1959 }
1960
man_udpSuccess(const Jid & streamHost)1961 void S5BConnector::man_udpSuccess(const Jid &streamHost)
1962 {
1963 // was anyone sending to this streamhost?
1964 foreach(Item *i, d->itemList) {
1965 if(i->host.jid().compare(streamHost) && i->client_udp) {
1966 i->udpSuccess();
1967 return;
1968 }
1969 }
1970 }
1971
1972 //----------------------------------------------------------------------------
1973 // S5BServer
1974 //----------------------------------------------------------------------------
1975 class S5BServer::Item : public QObject
1976 {
1977 Q_OBJECT
1978 public:
1979 SocksClient *client;
1980 QString host;
1981 QTimer expire;
1982
Item(SocksClient * c)1983 Item(SocksClient *c) : QObject(0)
1984 {
1985 client = c;
1986 connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
1987 connect(client, SIGNAL(incomingConnectRequest(QString,int)), SLOT(sc_incomingConnectRequest(QString,int)));
1988 connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1989
1990 connect(&expire, SIGNAL(timeout()), SLOT(doError()));
1991 resetExpiration();
1992 }
1993
~Item()1994 ~Item()
1995 {
1996 delete client;
1997 }
1998
resetExpiration()1999 void resetExpiration()
2000 {
2001 expire.start(30000);
2002 }
2003
2004 signals:
2005 void result(bool);
2006
2007 private slots:
doError()2008 void doError()
2009 {
2010 expire.stop();
2011 delete client;
2012 client = 0;
2013 result(false);
2014 }
2015
sc_incomingMethods(int m)2016 void sc_incomingMethods(int m)
2017 {
2018 if(m & SocksClient::AuthNone)
2019 client->chooseMethod(SocksClient::AuthNone);
2020 else
2021 doError();
2022 }
2023
sc_incomingConnectRequest(const QString & _host,int port)2024 void sc_incomingConnectRequest(const QString &_host, int port)
2025 {
2026 if(port == 0) {
2027 host = _host;
2028 client->disconnect(this);
2029 emit result(true);
2030 }
2031 else
2032 doError();
2033 }
2034
sc_error(int)2035 void sc_error(int)
2036 {
2037 doError();
2038 }
2039 };
2040
2041 class S5BServer::Private
2042 {
2043 public:
2044 SocksServer serv;
2045 QStringList hostList;
2046 QList<S5BManager*> manList;
2047 QList<Item*> itemList;
2048 };
2049
S5BServer(QObject * parent)2050 S5BServer::S5BServer(QObject *parent)
2051 :QObject(parent)
2052 {
2053 d = new Private;
2054 connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
2055 connect(&d->serv, SIGNAL(incomingUDP(QString,int,QHostAddress,int,QByteArray)), SLOT(ss_incomingUDP(QString,int,QHostAddress,int,QByteArray)));
2056 }
2057
~S5BServer()2058 S5BServer::~S5BServer()
2059 {
2060 unlinkAll();
2061 delete d;
2062 }
2063
isActive() const2064 bool S5BServer::isActive() const
2065 {
2066 return d->serv.isActive();
2067 }
2068
start(int port)2069 bool S5BServer::start(int port)
2070 {
2071 d->serv.stop();
2072 //return d->serv.listen(port, true);
2073 return d->serv.listen(port);
2074 }
2075
stop()2076 void S5BServer::stop()
2077 {
2078 d->serv.stop();
2079 }
2080
setHostList(const QStringList & list)2081 void S5BServer::setHostList(const QStringList &list)
2082 {
2083 d->hostList = list;
2084 }
2085
hostList() const2086 QStringList S5BServer::hostList() const
2087 {
2088 return d->hostList;
2089 }
2090
port() const2091 int S5BServer::port() const
2092 {
2093 return d->serv.port();
2094 }
2095
ss_incomingReady()2096 void S5BServer::ss_incomingReady()
2097 {
2098 Item *i = new Item(d->serv.takeIncoming());
2099 #ifdef S5B_DEBUG
2100 qDebug("S5BServer: incoming connection from %s:%d\n", qPrintable(i->client->peerAddress().toString()), i->client->peerPort());
2101 #endif
2102 connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
2103 d->itemList.append(i);
2104 }
2105
ss_incomingUDP(const QString & host,int port,const QHostAddress & addr,int sourcePort,const QByteArray & data)2106 void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data)
2107 {
2108 if(port != 0 && port != 1)
2109 return;
2110
2111 foreach(S5BManager* m, d->manList) {
2112 if(m->srv_ownsHash(host)) {
2113 m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data);
2114 return;
2115 }
2116 }
2117 }
2118
item_result(bool b)2119 void S5BServer::item_result(bool b)
2120 {
2121 Item *i = (Item *)sender();
2122 #ifdef S5B_DEBUG
2123 qDebug("S5BServer item result: %d\n", b);
2124 #endif
2125 if(!b) {
2126 d->itemList.removeAll(i);
2127 delete i;
2128 return;
2129 }
2130
2131 SocksClient *c = i->client;
2132 i->client = 0;
2133 QString key = i->host;
2134 d->itemList.removeAll(i);
2135 delete i;
2136
2137 // find the appropriate manager for this incoming connection
2138 foreach(S5BManager *m, d->manList) {
2139 if(m->srv_ownsHash(key)) {
2140 m->srv_incomingReady(c, key);
2141 return;
2142 }
2143 }
2144
2145 #ifdef S5B_DEBUG
2146 qDebug("S5BServer item result: unknown hash [%s]\n", qPrintable(key));
2147 #endif
2148
2149 // throw it away
2150 delete c;
2151 }
2152
link(S5BManager * m)2153 void S5BServer::link(S5BManager *m)
2154 {
2155 d->manList.append(m);
2156 }
2157
unlink(S5BManager * m)2158 void S5BServer::unlink(S5BManager *m)
2159 {
2160 d->manList.removeAll(m);
2161 }
2162
unlinkAll()2163 void S5BServer::unlinkAll()
2164 {
2165 foreach(S5BManager *m, d->manList) {
2166 m->srv_unlink();
2167 }
2168 d->manList.clear();
2169 }
2170
managerList() const2171 const QList<S5BManager*> & S5BServer::managerList() const
2172 {
2173 return d->manList;
2174 }
2175
writeUDP(const QHostAddress & addr,int port,const QByteArray & data)2176 void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
2177 {
2178 d->serv.writeUDP(addr, port, data);
2179 }
2180
2181 //----------------------------------------------------------------------------
2182 // JT_S5B
2183 //----------------------------------------------------------------------------
2184 class JT_S5B::Private
2185 {
2186 public:
2187 QDomElement iq;
2188 Jid to;
2189 Jid streamHost;
2190 StreamHost proxyInfo;
2191 int mode;
2192 QTimer t;
2193 };
2194
JT_S5B(Task * parent)2195 JT_S5B::JT_S5B(Task *parent)
2196 :Task(parent)
2197 {
2198 d = new Private;
2199 d->mode = -1;
2200 connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
2201 }
2202
~JT_S5B()2203 JT_S5B::~JT_S5B()
2204 {
2205 delete d;
2206 }
2207
request(const Jid & to,const QString & sid,const QString & dstaddr,const StreamHostList & hosts,bool fast,bool udp)2208 void JT_S5B::request(const Jid &to, const QString &sid, const QString &dstaddr,
2209 const StreamHostList &hosts, bool fast, bool udp)
2210 {
2211 d->mode = 0;
2212
2213 QDomElement iq;
2214 d->to = to;
2215 iq = createIQ(doc(), "set", to.full(), id());
2216 QDomElement query = doc()->createElement("query");
2217 query.setAttribute("xmlns", S5B_NS);
2218 query.setAttribute("sid", sid);
2219 if (!client()->groupChatNick(to.domain(), to.node()).isEmpty()) {
2220 query.setAttribute("dstaddr", dstaddr); // special case for muc as in xep-0065rc3
2221 }
2222 query.setAttribute("mode", udp ? "udp" : "tcp" );
2223 iq.appendChild(query);
2224 for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
2225 QDomElement shost = doc()->createElement("streamhost");
2226 shost.setAttribute("jid", (*it).jid().full());
2227 shost.setAttribute("host", (*it).host());
2228 shost.setAttribute("port", QString::number((*it).port()));
2229 if((*it).isProxy()) {
2230 QDomElement p = doc()->createElement("proxy");
2231 p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2232 shost.appendChild(p);
2233 }
2234 query.appendChild(shost);
2235 }
2236 if(fast) {
2237 QDomElement e = doc()->createElement("fast");
2238 e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2239 query.appendChild(e);
2240 }
2241 d->iq = iq;
2242 }
2243
requestProxyInfo(const Jid & to)2244 void JT_S5B::requestProxyInfo(const Jid &to)
2245 {
2246 d->mode = 1;
2247
2248 QDomElement iq;
2249 d->to = to;
2250 iq = createIQ(doc(), "get", to.full(), id());
2251 QDomElement query = doc()->createElement("query");
2252 query.setAttribute("xmlns", S5B_NS);
2253 iq.appendChild(query);
2254 d->iq = iq;
2255 }
2256
requestActivation(const Jid & to,const QString & sid,const Jid & target)2257 void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
2258 {
2259 d->mode = 2;
2260
2261 QDomElement iq;
2262 d->to = to;
2263 iq = createIQ(doc(), "set", to.full(), id());
2264 QDomElement query = doc()->createElement("query");
2265 query.setAttribute("xmlns", S5B_NS);
2266 query.setAttribute("sid", sid);
2267 iq.appendChild(query);
2268 QDomElement act = doc()->createElement("activate");
2269 act.appendChild(doc()->createTextNode(target.full()));
2270 query.appendChild(act);
2271 d->iq = iq;
2272 }
2273
onGo()2274 void JT_S5B::onGo()
2275 {
2276 if(d->mode == 1) {
2277 d->t.setSingleShot(true);
2278 d->t.start(15000);
2279 }
2280 send(d->iq);
2281 }
2282
onDisconnect()2283 void JT_S5B::onDisconnect()
2284 {
2285 d->t.stop();
2286 }
2287
take(const QDomElement & x)2288 bool JT_S5B::take(const QDomElement &x)
2289 {
2290 if(d->mode == -1)
2291 return false;
2292
2293 if(!iqVerify(x, d->to, id()))
2294 return false;
2295
2296 d->t.stop();
2297
2298 if(x.attribute("type") == "result") {
2299 QDomElement q = queryTag(x);
2300 if(d->mode == 0) {
2301 d->streamHost = "";
2302 if(!q.isNull()) {
2303 QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
2304 if(!shost.isNull())
2305 d->streamHost = shost.attribute("jid");
2306 }
2307
2308 setSuccess();
2309 }
2310 else if(d->mode == 1) {
2311 if(!q.isNull()) {
2312 QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
2313 if(!shost.isNull()) {
2314 Jid j = shost.attribute("jid");
2315 if(j.isValid()) {
2316 QString host = shost.attribute("host");
2317 if(!host.isEmpty()) {
2318 int port = shost.attribute("port").toInt();
2319 StreamHost h;
2320 h.setJid(j);
2321 h.setHost(host);
2322 h.setPort(port);
2323 h.setIsProxy(true);
2324 d->proxyInfo = h;
2325 }
2326 }
2327 }
2328 }
2329
2330 setSuccess();
2331 }
2332 else {
2333 setSuccess();
2334 }
2335 }
2336 else {
2337 setError(x);
2338 }
2339
2340 return true;
2341 }
2342
t_timeout()2343 void JT_S5B::t_timeout()
2344 {
2345 d->mode = -1;
2346 setError(500, "Timed out");
2347 }
2348
streamHostUsed() const2349 Jid JT_S5B::streamHostUsed() const
2350 {
2351 return d->streamHost;
2352 }
2353
proxyInfo() const2354 StreamHost JT_S5B::proxyInfo() const
2355 {
2356 return d->proxyInfo;
2357 }
2358
2359 //----------------------------------------------------------------------------
2360 // JT_PushS5B
2361 //----------------------------------------------------------------------------
JT_PushS5B(Task * parent)2362 JT_PushS5B::JT_PushS5B(Task *parent)
2363 :Task(parent)
2364 {
2365 }
2366
~JT_PushS5B()2367 JT_PushS5B::~JT_PushS5B()
2368 {
2369 }
2370
priority() const2371 int JT_PushS5B::priority() const
2372 {
2373 return 1;
2374 }
2375
take(const QDomElement & e)2376 bool JT_PushS5B::take(const QDomElement &e)
2377 {
2378 // look for udpsuccess
2379 if(e.tagName() == "message") {
2380 QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement();
2381 if(!x.isNull() && x.attribute("xmlns") == S5B_NS) {
2382 incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr"));
2383 return true;
2384 }
2385 x = e.elementsByTagName("activate").item(0).toElement();
2386 if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") {
2387 incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid")));
2388 return true;
2389 }
2390 return false;
2391 }
2392
2393 // must be an iq-set tag
2394 if(e.tagName() != "iq")
2395 return false;
2396 if(e.attribute("type") != "set")
2397 return false;
2398 if(queryNS(e) != S5B_NS)
2399 return false;
2400
2401 Jid from(e.attribute("from"));
2402 QDomElement q = queryTag(e);
2403 QString sid = q.attribute("sid");
2404
2405 StreamHostList hosts;
2406 QDomNodeList nl = q.elementsByTagName("streamhost");
2407 for(int n = 0; n < nl.count(); ++n) {
2408 QDomElement shost = nl.item(n).toElement();
2409 if(hosts.count() < MAXSTREAMHOSTS) {
2410 Jid j = shost.attribute("jid");
2411 if(!j.isValid())
2412 continue;
2413 QString host = shost.attribute("host");
2414 if(host.isEmpty())
2415 continue;
2416 int port = shost.attribute("port").toInt();
2417 QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
2418 bool isProxy = false;
2419 if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
2420 isProxy = true;
2421
2422 StreamHost h;
2423 h.setJid(j);
2424 h.setHost(host);
2425 h.setPort(port);
2426 h.setIsProxy(isProxy);
2427 hosts += h;
2428 }
2429 }
2430
2431 bool fast = false;
2432 QDomElement t;
2433 t = q.elementsByTagName("fast").item(0).toElement();
2434 if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
2435 fast = true;
2436
2437 S5BRequest r;
2438 r.from = from;
2439 r.id = e.attribute("id");
2440 r.sid = sid;
2441 r.dstaddr = q.attribute("dstaddr"); // special case for muc as in xep-0065rc3
2442 r.hosts = hosts;
2443 r.fast = fast;
2444 r.udp = q.attribute("mode") == "udp" ? true: false;
2445
2446 emit incoming(r);
2447 return true;
2448 }
2449
respondSuccess(const Jid & to,const QString & id,const Jid & streamHost)2450 void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
2451 {
2452 QDomElement iq = createIQ(doc(), "result", to.full(), id);
2453 QDomElement query = doc()->createElement("query");
2454 query.setAttribute("xmlns", S5B_NS);
2455 iq.appendChild(query);
2456 QDomElement shost = doc()->createElement("streamhost-used");
2457 shost.setAttribute("jid", streamHost.full());
2458 query.appendChild(shost);
2459 send(iq);
2460 }
2461
respondError(const Jid & to,const QString & id,Stanza::Error::ErrorCond cond,const QString & str)2462 void JT_PushS5B::respondError(const Jid &to, const QString &id,
2463 Stanza::Error::ErrorCond cond, const QString &str)
2464 {
2465 QDomElement iq = createIQ(doc(), "error", to.full(), id);
2466 Stanza::Error error(Stanza::Error::Cancel, cond, str);
2467 iq.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS()));
2468 send(iq);
2469 }
2470
sendUDPSuccess(const Jid & to,const QString & dstaddr)2471 void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr)
2472 {
2473 QDomElement m = doc()->createElement("message");
2474 m.setAttribute("to", to.full());
2475 QDomElement u = doc()->createElement("udpsuccess");
2476 u.setAttribute("xmlns", S5B_NS);
2477 u.setAttribute("dstaddr", dstaddr);
2478 m.appendChild(u);
2479 send(m);
2480 }
2481
sendActivate(const Jid & to,const QString & sid,const Jid & streamHost)2482 void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &streamHost)
2483 {
2484 QDomElement m = doc()->createElement("message");
2485 m.setAttribute("to", to.full());
2486 QDomElement act = doc()->createElement("activate");
2487 act.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2488 act.setAttribute("sid", sid);
2489 act.setAttribute("jid", streamHost.full());
2490 m.appendChild(act);
2491 send(m);
2492 }
2493
2494 //----------------------------------------------------------------------------
2495 // StreamHost
2496 //----------------------------------------------------------------------------
StreamHost()2497 StreamHost::StreamHost()
2498 {
2499 v_port = -1;
2500 proxy = false;
2501 }
2502
jid() const2503 const Jid & StreamHost::jid() const
2504 {
2505 return j;
2506 }
2507
host() const2508 const QString & StreamHost::host() const
2509 {
2510 return v_host;
2511 }
2512
port() const2513 int StreamHost::port() const
2514 {
2515 return v_port;
2516 }
2517
isProxy() const2518 bool StreamHost::isProxy() const
2519 {
2520 return proxy;
2521 }
2522
setJid(const Jid & _j)2523 void StreamHost::setJid(const Jid &_j)
2524 {
2525 j = _j;
2526 }
2527
setHost(const QString & host)2528 void StreamHost::setHost(const QString &host)
2529 {
2530 v_host = host;
2531 }
2532
setPort(int port)2533 void StreamHost::setPort(int port)
2534 {
2535 v_port = port;
2536 }
2537
setIsProxy(bool b)2538 void StreamHost::setIsProxy(bool b)
2539 {
2540 proxy = b;
2541 }
2542
2543 }
2544
2545 #include "s5b.moc"
2546