Fixed IBB and partially S5B in MUCs
[iris.git] / src / xmpp / xmpp-im / s5b.cpp
blob076fdd5b08de177d79b64c767676b138ca576618
1 /*
2 * s5b.cpp - direct connection protocol via tcp
3 * Copyright (C) 2003 Justin Karneges
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "s5b.h"
23 #include <QTimer>
24 #include <QPointer>
25 #include <QByteArray>
26 #include <stdlib.h>
27 #include <qca.h>
28 #include "xmpp_xmlcommon.h"
29 #include "im.h"
30 #include "socks.h"
31 #include "safedelete.h"
33 #ifdef Q_OS_WIN
34 # include <windows.h>
35 #else
36 # include <netinet/in.h>
37 #endif
39 #define MAXSTREAMHOSTS 5
41 static const char *S5B_NS = "http://jabber.org/protocol/bytestreams";
43 //#define S5B_DEBUG
45 namespace XMPP {
47 static QString makeKey(const QString &sid, const Jid &initiator, const Jid &target)
49 #ifdef S5B_DEBUG
50 qDebug("makeKey: sid=%s initiator=%s target=%s", qPrintable(sid),
51 qPrintable(initiator.full()), qPrintable(target.full()));
52 #endif
53 QString str = sid + initiator.full() + target.full();
54 return QCA::Hash("sha1").hashToString(str.toUtf8());
57 static bool haveHost(const StreamHostList &list, const Jid &j)
59 for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
60 if((*it).jid().compare(j))
61 return true;
63 return false;
66 class S5BManager::Item : public QObject
68 Q_OBJECT
69 public:
70 enum { Idle, Initiator, 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; // initiator 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;
95 Item(S5BManager *manager);
96 ~Item();
98 void reset();
99 void startInitiator(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, const StreamHostList &hosts, const QString &iq_id, bool fast, bool udp);
101 void handleFast(const StreamHostList &hosts, const QString &iq_id);
103 void doOutgoing();
104 void doIncoming();
105 void setIncomingClient(SocksClient *sc);
106 void incomingActivate(const Jid &streamHost);
108 signals:
109 void accepted();
110 void tryingHosts(const StreamHostList &list);
111 void proxyConnect();
112 void waitingForActivation();
113 void connected();
114 void error(int);
116 private slots:
117 void jt_finished();
118 void conn_result(bool b);
119 void proxy_result(bool b);
120 void proxy_finished();
121 void sc_readyRead();
122 void sc_bytesWritten(int);
123 void sc_error(int);
125 private:
126 void doConnectError();
127 void tryActivation();
128 void checkForActivation();
129 void checkFailure();
130 void finished();
133 //----------------------------------------------------------------------------
134 // S5BDatagram
135 //----------------------------------------------------------------------------
136 S5BDatagram::S5BDatagram()
138 _source = 0;
139 _dest = 0;
142 S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data)
144 _source = source;
145 _dest = dest;
146 _buf = data;
149 int S5BDatagram::sourcePort() const
151 return _source;
154 int S5BDatagram::destPort() const
156 return _dest;
159 QByteArray S5BDatagram::data() const
161 return _buf;
164 //----------------------------------------------------------------------------
165 // S5BConnection
166 //----------------------------------------------------------------------------
167 class S5BConnection::Private
169 public:
170 S5BManager *m;
171 SocksClient *sc;
172 SocksUDP *su;
173 int state;
174 Jid peer;
175 QString sid;
176 bool remote;
177 bool switched;
178 bool notifyRead, notifyClose;
179 int id;
180 S5BRequest req;
181 Jid proxy;
182 Mode mode;
183 QList<S5BDatagram*> dglist;
186 static int id_conn = 0;
187 static int num_conn = 0;
189 S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
190 : BSConnection(parent)
192 d = new Private;
193 d->m = m;
194 d->sc = 0;
195 d->su = 0;
197 ++num_conn;
198 d->id = id_conn++;
199 #ifdef S5B_DEBUG
200 qDebug("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
201 #endif
203 reset();
206 S5BConnection::~S5BConnection()
208 reset(true);
210 --num_conn;
211 #ifdef S5B_DEBUG
212 qDebug("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
213 #endif
215 delete d;
218 void S5BConnection::reset(bool clear)
220 d->m->con_unlink(this);
221 if(clear && d->sc) {
222 delete d->sc;
223 d->sc = 0;
225 delete d->su;
226 d->su = 0;
227 if(clear) {
228 while (!d->dglist.isEmpty()) {
229 delete d->dglist.takeFirst();
232 d->state = Idle;
233 d->peer = Jid();
234 d->sid = QString();
235 d->remote = false;
236 d->switched = false;
237 d->notifyRead = false;
238 d->notifyClose = false;
241 Jid S5BConnection::proxy() const
243 return d->proxy;
246 void S5BConnection::setProxy(const Jid &proxy)
248 d->proxy = proxy;
251 void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m)
253 reset(true);
254 if(!d->m->isAcceptableSID(peer, sid))
255 return;
257 d->peer = peer;
258 d->sid = sid;
259 d->state = Requesting;
260 d->mode = m;
261 #ifdef S5B_DEBUG
262 qDebug("S5BConnection[%d]: connecting %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
263 #endif
264 d->m->con_connect(this);
267 void S5BConnection::accept()
269 if(d->state != WaitingForAccept)
270 return;
272 d->state = Connecting;
273 #ifdef S5B_DEBUG
274 qDebug("S5BConnection[%d]: accepting %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
275 #endif
276 d->m->con_accept(this);
279 void S5BConnection::close()
281 if(d->state == Idle)
282 return;
284 if(d->state == WaitingForAccept)
285 d->m->con_reject(this);
286 else if(d->state == Active)
287 d->sc->close();
288 #ifdef S5B_DEBUG
289 qDebug("S5BConnection[%d]: closing %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
290 #endif
291 reset();
294 Jid S5BConnection::peer() const
296 return d->peer;
299 QString S5BConnection::sid() const
301 return d->sid;
304 BytestreamManager* S5BConnection::manager() const
306 return d->m;
309 bool S5BConnection::isRemote() const
311 return d->remote;
314 S5BConnection::Mode S5BConnection::mode() const
316 return d->mode;
319 int S5BConnection::state() const
321 return d->state;
324 bool S5BConnection::isOpen() const
326 if(d->state == Active)
327 return true;
328 else
329 return false;
332 void S5BConnection::write(const QByteArray &buf)
334 if(d->state == Active && d->mode == Stream)
335 d->sc->write(buf);
338 QByteArray S5BConnection::read(int bytes)
340 if(d->sc)
341 return d->sc->read(bytes);
342 else
343 return QByteArray();
346 int S5BConnection::bytesAvailable() const
348 if(d->sc)
349 return d->sc->bytesAvailable();
350 else
351 return 0;
354 int S5BConnection::bytesToWrite() const
356 if(d->state == Active)
357 return d->sc->bytesToWrite();
358 else
359 return 0;
362 void S5BConnection::writeDatagram(const S5BDatagram &i)
364 QByteArray buf;
365 buf.resize(i.data().size() + 4);
366 ushort ssp = htons(i.sourcePort());
367 ushort sdp = htons(i.destPort());
368 QByteArray data = i.data();
369 memcpy(buf.data(), &ssp, 2);
370 memcpy(buf.data() + 2, &sdp, 2);
371 memcpy(buf.data() + 4, data.data(), data.size());
372 sendUDP(buf);
375 S5BDatagram S5BConnection::readDatagram()
377 if(d->dglist.isEmpty())
378 return S5BDatagram();
379 S5BDatagram *i = d->dglist.takeFirst();
380 S5BDatagram val = *i;
381 delete i;
382 return val;
385 int S5BConnection::datagramsAvailable() const
387 return d->dglist.count();
390 void S5BConnection::man_waitForAccept(const S5BRequest &r)
392 d->state = WaitingForAccept;
393 d->remote = true;
394 d->req = r;
395 d->peer = r.from;
396 d->sid = r.sid;
397 d->mode = r.udp ? Datagram : Stream;
400 void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp)
402 d->sc = sc;
403 connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
404 connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
405 connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
406 connect(d->sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
407 connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
409 if(sc_udp) {
410 d->su = sc_udp;
411 connect(d->su, SIGNAL(packetReady(const QByteArray &)), SLOT(su_packetReady(const QByteArray &)));
414 d->state = Active;
415 #ifdef S5B_DEBUG
416 qDebug("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid));
417 #endif
419 // bytes already in the stream?
420 if(d->sc->bytesAvailable()) {
421 #ifdef S5B_DEBUG
422 qDebug("Stream has %d bytes in it.\n", d->sc->bytesAvailable());
423 #endif
424 d->notifyRead = true;
426 // closed before it got here?
427 if(!d->sc->isOpen()) {
428 #ifdef S5B_DEBUG
429 qDebug("Stream was closed before S5B request finished?\n");
430 #endif
431 d->notifyClose = true;
433 if(d->notifyRead || d->notifyClose)
434 QTimer::singleShot(0, this, SLOT(doPending()));
435 connected();
438 void S5BConnection::doPending()
440 if(d->notifyRead) {
441 if(d->notifyClose)
442 QTimer::singleShot(0, this, SLOT(doPending()));
443 sc_readyRead();
445 else if(d->notifyClose)
446 sc_connectionClosed();
449 void S5BConnection::man_udpReady(const QByteArray &buf)
451 handleUDP(buf);
454 void S5BConnection::man_failed(int x)
456 reset(true);
457 if(x == S5BManager::Item::ErrRefused)
458 error(ErrRefused);
459 if(x == S5BManager::Item::ErrConnect)
460 error(ErrConnect);
461 if(x == S5BManager::Item::ErrWrongHost)
462 error(ErrConnect);
463 if(x == S5BManager::Item::ErrProxy)
464 error(ErrProxy);
467 void S5BConnection::sc_connectionClosed()
469 // if we have a pending read notification, postpone close
470 if(d->notifyRead) {
471 #ifdef S5B_DEBUG
472 qDebug("closed while pending read\n");
473 #endif
474 d->notifyClose = true;
475 return;
477 d->notifyClose = false;
478 reset();
479 connectionClosed();
482 void S5BConnection::sc_delayedCloseFinished()
484 // echo
485 delayedCloseFinished();
488 void S5BConnection::sc_readyRead()
490 if(d->mode == Datagram) {
491 // throw the data away
492 d->sc->read();
493 return;
496 d->notifyRead = false;
497 // echo
498 readyRead();
501 void S5BConnection::sc_bytesWritten(int x)
503 // echo
504 bytesWritten(x);
507 void S5BConnection::sc_error(int)
509 reset();
510 error(ErrSocket);
513 void S5BConnection::su_packetReady(const QByteArray &buf)
515 handleUDP(buf);
518 void S5BConnection::handleUDP(const QByteArray &buf)
520 // must be at least 4 bytes, to accomodate virtual ports
521 if(buf.size() < 4)
522 return; // drop
524 ushort ssp, sdp;
525 memcpy(&ssp, buf.data(), 2);
526 memcpy(&sdp, buf.data() + 2, 2);
527 int source = ntohs(ssp);
528 int dest = ntohs(sdp);
529 QByteArray data;
530 data.resize(buf.size() - 4);
531 memcpy(data.data(), buf.data() + 4, data.size());
532 d->dglist.append(new S5BDatagram(source, dest, data));
534 datagramReady();
537 void S5BConnection::sendUDP(const QByteArray &buf)
539 if(d->su)
540 d->su->write(buf);
541 else
542 d->m->con_sendUDP(this, buf);
545 //----------------------------------------------------------------------------
546 // S5BManager
547 //----------------------------------------------------------------------------
548 class S5BManager::Entry
550 public:
551 Entry()
553 i = 0;
554 query = 0;
555 udp_init = false;
558 ~Entry()
560 delete query;
563 S5BConnection *c;
564 Item *i;
565 QString sid;
566 JT_S5B *query;
567 StreamHost proxyInfo;
568 QPointer<S5BServer> relatedServer;
570 bool udp_init;
571 QHostAddress udp_addr;
572 int udp_port;
575 class S5BManager::Private
577 public:
578 Client *client;
579 S5BServer *serv;
580 QList<Entry*> activeList;
581 S5BConnectionList incomingConns;
582 JT_PushS5B *ps;
585 S5BManager::S5BManager(Client *parent)
586 : BytestreamManager(parent)
588 // S5B needs SHA1
589 //if(!QCA::isSupported(QCA::CAP_SHA1))
590 // QCA::insertProvider(createProviderHash());
592 d = new Private;
593 d->client = parent;
594 d->serv = 0;
596 d->ps = new JT_PushS5B(d->client->rootTask());
597 connect(d->ps, SIGNAL(incoming(const S5BRequest &)), SLOT(ps_incoming(const S5BRequest &)));
598 connect(d->ps, SIGNAL(incomingUDPSuccess(const Jid &, const QString &)), SLOT(ps_incomingUDPSuccess(const Jid &, const QString &)));
599 connect(d->ps, SIGNAL(incomingActivate(const Jid &, const QString &, const Jid &)), SLOT(ps_incomingActivate(const Jid &, const QString &, const Jid &)));
602 S5BManager::~S5BManager()
604 setServer(0);
605 while (!d->incomingConns.isEmpty()) {
606 delete d->incomingConns.takeFirst();
608 delete d->ps;
609 delete d;
612 const char* S5BManager::ns()
614 return S5B_NS;
617 Client *S5BManager::client() const
619 return d->client;
622 S5BServer *S5BManager::server() const
624 return d->serv;
627 void S5BManager::setServer(S5BServer *serv)
629 if(d->serv) {
630 d->serv->unlink(this);
631 d->serv = 0;
634 if(serv) {
635 d->serv = serv;
636 d->serv->link(this);
640 S5BConnection *S5BManager::createConnection()
642 return new S5BConnection(this);
645 S5BConnection *S5BManager::takeIncoming()
647 if(d->incomingConns.isEmpty())
648 return 0;
650 S5BConnection *c = d->incomingConns.takeFirst();
652 // move to activeList
653 Entry *e = new Entry;
654 e->c = c;
655 e->sid = c->d->sid;
656 d->activeList.append(e);
658 return c;
661 void S5BManager::ps_incoming(const S5BRequest &req)
663 #ifdef S5B_DEBUG
664 qDebug("S5BManager: incoming from %s\n", qPrintable(req.from.full()));
665 #endif
667 bool ok = false;
668 // ensure we don't already have an incoming connection from this peer+sid
669 S5BConnection *c = findIncoming(req.from, req.sid);
670 if(!c) {
671 // do we have an active entry with this sid already?
672 Entry *e = findEntryBySID(req.from, req.sid);
673 if(e) {
674 if(e->i) {
675 // loopback
676 if(req.from.compare(localPeer(req.from)) && (req.id == e->i->out_id)) {
677 #ifdef S5B_DEBUG
678 qDebug("ALLOWED: loopback\n");
679 #endif
680 ok = true;
682 // allowed by 'fast mode'
683 else if(e->i->state == Item::Initiator && e->i->targetMode == Item::Unknown) {
684 #ifdef S5B_DEBUG
685 qDebug("ALLOWED: fast-mode\n");
686 #endif
687 e->i->handleFast(req.hosts, req.id);
688 return;
692 else {
693 #ifdef S5B_DEBUG
694 qDebug("ALLOWED: we don't have it\n");
695 #endif
696 ok = true;
699 if(!ok) {
700 d->ps->respondError(req.from, req.id, Stanza::Error::NotAcceptable, "SID in use");
701 return;
704 // create an incoming connection
705 c = new S5BConnection(this);
706 c->man_waitForAccept(req);
707 d->incomingConns.append(c);
708 incomingReady();
711 void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key)
713 Entry *e = findEntryByHash(key);
714 if(e && e->i) {
715 if(e->i->conn)
716 e->i->conn->man_udpSuccess(from);
717 else if(e->i->proxy_conn)
718 e->i->proxy_conn->man_udpSuccess(from);
722 void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost)
724 Entry *e = findEntryBySID(from, sid);
725 if(e && e->i)
726 e->i->incomingActivate(streamHost);
729 void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
731 d->ps->respondSuccess(peer, id, streamHost);
734 void S5BManager::doError(const Jid &peer, const QString &id,
735 Stanza::Error::ErrorCond cond, const QString &str)
737 d->ps->respondError(peer, id, cond, str);
740 void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &streamHost)
742 d->ps->sendActivate(peer, sid, streamHost);
745 Jid S5BManager::localPeer(const Jid &peer) const
747 QString gcNick = d->client->groupChatNick(peer.domain(), peer.node());
748 return gcNick.isEmpty() ? d->client->jid() : peer.withResource(gcNick);
751 bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
753 Jid lp = localPeer(peer);
754 QString key = makeKey(sid, lp, peer);
755 QString key_out = makeKey(sid, peer, lp);
757 // if we have a server, then check through it
758 if(d->serv) {
759 if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
760 return false;
762 else {
763 if(findEntryByHash(key) || findEntryByHash(key_out))
764 return false;
766 return true;
769 const char* S5BManager::sidPrefix() const
771 return "s5b_";
774 S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
776 foreach(S5BConnection *c, d->incomingConns) {
777 if(c->d->peer.compare(from) && c->d->sid == sid)
778 return c;
780 return 0;
783 S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
785 foreach(Entry *e, d->activeList) {
786 if(e->c == c)
787 return e;
789 return 0;
792 S5BManager::Entry *S5BManager::findEntry(Item *i) const
794 foreach(Entry *e, d->activeList) {
795 if(e->i == i)
796 return e;
798 return 0;
801 S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
803 foreach(Entry *e, d->activeList) {
804 if(e->i && e->i->key == key)
805 return e;
807 return 0;
810 S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
812 foreach(Entry *e, d->activeList) {
813 if(e->i && e->i->peer.compare(peer) && e->sid == sid)
814 return e;
816 return 0;
819 S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
821 const QList<S5BManager*> &manList = d->serv->managerList();
822 foreach(S5BManager *m, manList) {
823 Entry *e = m->findEntryByHash(key);
824 if(e)
825 return e;
827 return 0;
830 bool S5BManager::srv_ownsHash(const QString &key) const
832 if(findEntryByHash(key))
833 return true;
834 return false;
837 void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
839 Entry *e = findEntryByHash(key);
840 if(!e->i->allowIncoming) {
841 sc->requestDeny();
842 SafeDelete::deleteSingle(sc);
843 return;
845 if(e->c->d->mode == S5BConnection::Datagram)
846 sc->grantUDPAssociate("", 0);
847 else
848 sc->grantConnect();
849 e->relatedServer = (S5BServer *)sender();
850 e->i->setIncomingClient(sc);
853 void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data)
855 Entry *e = findEntryByHash(key);
856 if(!e->c->d->mode != S5BConnection::Datagram)
857 return; // this key isn't in udp mode? drop!
859 if(init) {
860 if(e->udp_init)
861 return; // only init once
863 // lock on to this sender
864 e->udp_addr = addr;
865 e->udp_port = port;
866 e->udp_init = true;
868 // reply that initialization was successful
869 d->ps->sendUDPSuccess(e->c->d->peer, key);
870 return;
873 // not initialized yet? something went wrong
874 if(!e->udp_init)
875 return;
877 // must come from same source as when initialized
878 if(addr.toString() != e->udp_addr.toString() || port != e->udp_port)
879 return;
881 e->c->man_udpReady(data);
884 void S5BManager::srv_unlink()
886 d->serv = 0;
889 void S5BManager::con_connect(S5BConnection *c)
891 if(findEntry(c))
892 return;
893 Entry *e = new Entry;
894 e->c = c;
895 e->sid = c->d->sid;
896 d->activeList.append(e);
898 if(c->d->proxy.isValid()) {
899 queryProxy(e);
900 return;
902 entryContinue(e);
905 void S5BManager::con_accept(S5BConnection *c)
907 Entry *e = findEntry(c);
908 if(!e)
909 return;
911 if(e->c->d->req.fast) {
912 if(targetShouldOfferProxy(e)) {
913 queryProxy(e);
914 return;
917 entryContinue(e);
920 void S5BManager::con_reject(S5BConnection *c)
922 d->ps->respondError(c->d->peer, c->d->req.id, Stanza::Error::NotAcceptable,
923 "Not acceptable");
926 void S5BManager::con_unlink(S5BConnection *c)
928 Entry *e = findEntry(c);
929 if(!e)
930 return;
932 // active incoming request? cancel it
933 if(e->i && e->i->conn)
934 d->ps->respondError(e->i->peer, e->i->out_id,
935 Stanza::Error::NotAcceptable, "Not acceptable");
936 delete e->i;
937 d->activeList.removeAll(e);
938 delete e;
941 void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf)
943 Entry *e = findEntry(c);
944 if(!e)
945 return;
946 if(!e->udp_init)
947 return;
949 if(e->relatedServer)
950 e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf);
953 void S5BManager::item_accepted()
955 Item *i = (Item *)sender();
956 Entry *e = findEntry(i);
958 emit e->c->accepted(); // signal
961 void S5BManager::item_tryingHosts(const StreamHostList &list)
963 Item *i = (Item *)sender();
964 Entry *e = findEntry(i);
966 e->c->tryingHosts(list); // signal
969 void S5BManager::item_proxyConnect()
971 Item *i = (Item *)sender();
972 Entry *e = findEntry(i);
974 e->c->proxyConnect(); // signal
977 void S5BManager::item_waitingForActivation()
979 Item *i = (Item *)sender();
980 Entry *e = findEntry(i);
982 e->c->waitingForActivation(); // signal
985 void S5BManager::item_connected()
987 Item *i = (Item *)sender();
988 Entry *e = findEntry(i);
990 // grab the client
991 SocksClient *client = i->client;
992 i->client = 0;
993 SocksUDP *client_udp = i->client_udp;
994 i->client_udp = 0;
996 // give it to the connection
997 e->c->man_clientReady(client, client_udp);
1000 void S5BManager::item_error(int x)
1002 Item *i = (Item *)sender();
1003 Entry *e = findEntry(i);
1005 e->c->man_failed(x);
1008 void S5BManager::entryContinue(Entry *e)
1010 e->i = new Item(this);
1011 e->i->proxy = e->proxyInfo;
1013 connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
1014 connect(e->i, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(item_tryingHosts(const StreamHostList &)));
1015 connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
1016 connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
1017 connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
1018 connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
1020 if(e->c->isRemote()) {
1021 const S5BRequest &req = e->c->d->req;
1022 e->i->startTarget(e->sid, localPeer(e->c->d->peer), e->c->d->peer, req.hosts, req.id, req.fast, req.udp);
1024 else {
1025 e->i->startInitiator(e->sid, localPeer(e->c->d->peer), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false);
1026 e->c->requesting(); // signal
1030 void S5BManager::queryProxy(Entry *e)
1032 QPointer<QObject> self = this;
1033 e->c->proxyQuery(); // signal
1034 if(!self)
1035 return;
1037 #ifdef S5B_DEBUG
1038 qDebug("querying proxy: [%s]\n", qPrintable(e->c->d->proxy.full()));
1039 #endif
1040 e->query = new JT_S5B(d->client->rootTask());
1041 connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
1042 e->query->requestProxyInfo(e->c->d->proxy);
1043 e->query->go(true);
1046 void S5BManager::query_finished()
1048 JT_S5B *query = (JT_S5B *)sender();
1049 Entry* e = 0;
1050 foreach(Entry* i, d->activeList) {
1051 if(i->query == query) {
1052 e = i;
1053 break;
1056 if(!e)
1057 return;
1058 e->query = 0;
1060 #ifdef S5B_DEBUG
1061 qDebug("query finished: ");
1062 #endif
1063 if(query->success()) {
1064 e->proxyInfo = query->proxyInfo();
1065 #ifdef S5B_DEBUG
1066 qDebug("host/ip=[%s] port=[%d]\n", qPrintable(e->proxyInfo.host()), e->proxyInfo.port());
1067 #endif
1069 else {
1070 #ifdef S5B_DEBUG
1071 qDebug("fail\n");
1072 #endif
1075 QPointer<QObject> self = this;
1076 e->c->proxyResult(query->success()); // signal
1077 if(!self)
1078 return;
1080 entryContinue(e);
1083 bool S5BManager::targetShouldOfferProxy(Entry *e)
1085 if(!e->c->d->proxy.isValid())
1086 return false;
1088 // if target, don't offer any proxy if the initiator already did
1089 const StreamHostList &hosts = e->c->d->req.hosts;
1090 for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1091 if((*it).isProxy())
1092 return false;
1095 // ensure we don't offer the same proxy as the initiator
1096 if(haveHost(hosts, e->c->d->proxy))
1097 return false;
1099 return true;
1102 //----------------------------------------------------------------------------
1103 // S5BManager::Item
1104 //----------------------------------------------------------------------------
1105 S5BManager::Item::Item(S5BManager *manager) : QObject(0)
1107 m = manager;
1108 task = 0;
1109 proxy_task = 0;
1110 conn = 0;
1111 proxy_conn = 0;
1112 client_udp = 0;
1113 client = 0;
1114 client_out_udp = 0;
1115 client_out = 0;
1116 reset();
1119 S5BManager::Item::~Item()
1121 reset();
1124 void S5BManager::Item::reset()
1126 delete task;
1127 task = 0;
1129 delete proxy_task;
1130 proxy_task = 0;
1132 delete conn;
1133 conn = 0;
1135 delete proxy_conn;
1136 proxy_conn = 0;
1138 delete client_udp;
1139 client_udp = 0;
1141 delete client;
1142 client = 0;
1144 delete client_out_udp;
1145 client_out_udp = 0;
1147 delete client_out;
1148 client_out = 0;
1150 state = Idle;
1151 wantFast = false;
1152 targetMode = Unknown;
1153 fast = false;
1154 activated = false;
1155 lateProxy = false;
1156 connSuccess = false;
1157 localFailed = false;
1158 remoteFailed = false;
1159 allowIncoming = false;
1160 udp = false;
1163 void S5BManager::Item::startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp)
1165 sid = _sid;
1166 self = _self;
1167 peer = _peer;
1168 key = makeKey(sid, self, peer);
1169 out_key = makeKey(sid, peer, self);
1170 wantFast = fast;
1171 udp = _udp;
1173 #ifdef S5B_DEBUG
1174 qDebug("S5BManager::Item initiating request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key));
1175 #endif
1176 state = Initiator;
1177 doOutgoing();
1180 void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool _fast, bool _udp)
1182 sid = _sid;
1183 peer = _peer;
1184 self = _self;
1185 in_hosts = hosts;
1186 in_id = iq_id;
1187 fast = _fast;
1188 key = makeKey(sid, self, peer);
1189 out_key = makeKey(sid, peer, self);
1190 udp = _udp;
1192 #ifdef S5B_DEBUG
1193 qDebug("S5BManager::Item incoming request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key));
1194 #endif
1195 state = Target;
1196 if(fast)
1197 doOutgoing();
1198 doIncoming();
1201 void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
1203 targetMode = Fast;
1205 QPointer<QObject> self = this;
1206 emit accepted();
1207 if(!self)
1208 return;
1210 // if we already have a stream, then bounce this request
1211 if(client) {
1212 m->doError(peer, iq_id, Stanza::Error::NotAcceptable, "Not acceptable");
1214 else {
1215 in_hosts = hosts;
1216 in_id = iq_id;
1217 doIncoming();
1221 void S5BManager::Item::doOutgoing()
1223 StreamHostList hosts;
1224 S5BServer *serv = m->server();
1225 if(serv && serv->isActive() && !haveHost(in_hosts, self)) {
1226 QStringList hostList = serv->hostList();
1227 for(QStringList::ConstIterator it = hostList.begin(); it != hostList.end(); ++it) {
1228 StreamHost h;
1229 h.setJid(self);
1230 h.setHost(*it);
1231 h.setPort(serv->port());
1232 hosts += h;
1236 // if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
1237 if(proxy.jid().isValid())
1238 hosts += proxy;
1240 // if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
1241 if(state == Target && hosts.isEmpty()) {
1242 fast = false;
1243 return;
1246 allowIncoming = true;
1248 task = new JT_S5B(m->client()->rootTask());
1249 connect(task, SIGNAL(finished()), SLOT(jt_finished()));
1250 task->request(peer, sid, hosts, state == Initiator ? wantFast : false, udp);
1251 out_id = task->id();
1252 task->go(true);
1255 void S5BManager::Item::doIncoming()
1257 if(in_hosts.isEmpty()) {
1258 doConnectError();
1259 return;
1262 StreamHostList list;
1263 if(lateProxy) {
1264 // take just the proxy streamhosts
1265 for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
1266 if((*it).isProxy())
1267 list += *it;
1269 lateProxy = false;
1271 else {
1272 // only try doing the late proxy trick if using fast mode AND we did not offer a proxy
1273 if((state == Initiator || (state == Target && fast)) && !proxy.jid().isValid()) {
1274 // take just the non-proxy streamhosts
1275 bool hasProxies = false;
1276 for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
1277 if((*it).isProxy())
1278 hasProxies = true;
1279 else
1280 list += *it;
1282 if(hasProxies) {
1283 lateProxy = true;
1285 // no regular streamhosts? wait for remote error
1286 if(list.isEmpty())
1287 return;
1290 else
1291 list = in_hosts;
1294 conn = new S5BConnector;
1295 connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
1297 QPointer<QObject> self = this;
1298 tryingHosts(list);
1299 if(!self)
1300 return;
1302 conn->start(this->self, list, out_key, udp, lateProxy ? 10 : 30);
1305 void S5BManager::Item::setIncomingClient(SocksClient *sc)
1307 #ifdef S5B_DEBUG
1308 qDebug("S5BManager::Item: %s [%s] successful incoming connection\n", qPrintable(peer.full()), qPrintable(sid));
1309 #endif
1311 connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1312 connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1313 connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1315 client = sc;
1316 allowIncoming = false;
1319 void S5BManager::Item::incomingActivate(const Jid &streamHost)
1321 if(!activated) {
1322 activatedStream = streamHost;
1323 checkForActivation();
1327 void S5BManager::Item::jt_finished()
1329 JT_S5B *j = task;
1330 task = 0;
1332 #ifdef S5B_DEBUG
1333 qDebug("jt_finished: state=%s, %s\n", state == Initiator ? "initiator" : "target", j->success() ? "ok" : "fail");
1334 #endif
1336 if(state == Initiator) {
1337 if(targetMode == Unknown) {
1338 targetMode = NotFast;
1339 QPointer<QObject> self = this;
1340 emit accepted();
1341 if(!self)
1342 return;
1346 // if we've already reported successfully connecting to them, then this response doesn't matter
1347 if(state == Initiator && connSuccess) {
1348 tryActivation();
1349 return;
1352 if(j->success()) {
1353 // stop connecting out
1354 if(conn || lateProxy) {
1355 delete conn;
1356 conn = 0;
1357 doConnectError();
1360 Jid streamHost = j->streamHostUsed();
1362 // they connected to us?
1363 if(streamHost.compare(self)) {
1364 if(client) {
1365 if(state == Initiator) {
1366 activatedStream = streamHost;
1367 tryActivation();
1369 else
1370 checkForActivation();
1372 else {
1373 #ifdef S5B_DEBUG
1374 qDebug("S5BManager::Item %s claims to have connected to us, but we don't see this\n", qPrintable(peer.full()));
1375 #endif
1376 reset();
1377 error(ErrWrongHost);
1380 else if(streamHost.compare(proxy.jid())) {
1381 // toss out any direct incoming, since it won't be used
1382 delete client;
1383 client = 0;
1384 allowIncoming = false;
1386 #ifdef S5B_DEBUG
1387 qDebug("attempting to connect to proxy\n");
1388 #endif
1389 // connect to the proxy
1390 proxy_conn = new S5BConnector;
1391 connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
1392 StreamHostList list;
1393 list += proxy;
1395 QPointer<QObject> self = this;
1396 proxyConnect();
1397 if(!self)
1398 return;
1400 proxy_conn->start(this->self, list, key, udp, 30);
1402 else {
1403 #ifdef S5B_DEBUG
1404 qDebug("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", qPrintable(peer.full()));
1405 #endif
1406 reset();
1407 error(ErrWrongHost);
1410 else {
1411 #ifdef S5B_DEBUG
1412 qDebug("S5BManager::Item %s [%s] error\n", qPrintable(peer.full()), qPrintable(sid));
1413 #endif
1414 remoteFailed = true;
1415 statusCode = j->statusCode();
1417 if(lateProxy) {
1418 if(!conn)
1419 doIncoming();
1421 else {
1422 // if connSuccess is true at this point, then we're a Target
1423 if(connSuccess)
1424 checkForActivation();
1425 else
1426 checkFailure();
1431 void S5BManager::Item::conn_result(bool b)
1433 if(b) {
1434 SocksClient *sc = conn->takeClient();
1435 SocksUDP *sc_udp = conn->takeUDP();
1436 StreamHost h = conn->streamHostUsed();
1437 delete conn;
1438 conn = 0;
1439 connSuccess = true;
1441 #ifdef S5B_DEBUG
1442 qDebug("S5BManager::Item: %s [%s] successful outgoing connection\n",
1443 qPrintable(peer.full()), qPrintable(sid));
1444 #endif
1446 connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1447 connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1448 connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1450 m->doSuccess(peer, in_id, h.jid());
1452 // if the first batch works, don't try proxy
1453 lateProxy = false;
1455 // if initiator, run with this one
1456 if(state == Initiator) {
1457 // if we had an incoming one, toss it
1458 delete client_udp;
1459 client_udp = sc_udp;
1460 delete client;
1461 client = sc;
1462 allowIncoming = false;
1463 activatedStream = peer;
1464 tryActivation();
1466 else {
1467 client_out_udp = sc_udp;
1468 client_out = sc;
1469 checkForActivation();
1472 else {
1473 delete conn;
1474 conn = 0;
1476 // if we delayed the proxies for later, try now
1477 if(lateProxy) {
1478 if(remoteFailed)
1479 doIncoming();
1481 else
1482 doConnectError();
1486 void S5BManager::Item::proxy_result(bool b)
1488 #ifdef S5B_DEBUG
1489 qDebug("proxy_result: %s\n", b ? "ok" : "fail");
1490 #endif
1491 if(b) {
1492 SocksClient *sc = proxy_conn->takeClient();
1493 SocksUDP *sc_udp = proxy_conn->takeUDP();
1494 delete proxy_conn;
1495 proxy_conn = 0;
1497 connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1498 connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1499 connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1501 client = sc;
1502 client_udp = sc_udp;
1504 // activate
1505 #ifdef S5B_DEBUG
1506 qDebug("activating proxy stream\n");
1507 #endif
1508 proxy_task = new JT_S5B(m->client()->rootTask());
1509 connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
1510 proxy_task->requestActivation(proxy.jid(), sid, peer);
1511 proxy_task->go(true);
1513 else {
1514 delete proxy_conn;
1515 proxy_conn = 0;
1516 reset();
1517 error(ErrProxy);
1521 void S5BManager::Item::proxy_finished()
1523 JT_S5B *j = proxy_task;
1524 proxy_task = 0;
1526 if(j->success()) {
1527 #ifdef S5B_DEBUG
1528 qDebug("proxy stream activated\n");
1529 #endif
1530 if(state == Initiator) {
1531 activatedStream = proxy.jid();
1532 tryActivation();
1534 else
1535 checkForActivation();
1537 else {
1538 reset();
1539 error(ErrProxy);
1543 void S5BManager::Item::sc_readyRead()
1545 #ifdef S5B_DEBUG
1546 qDebug("sc_readyRead\n");
1547 #endif
1548 // only targets check for activation, and only should do it if there is no pending outgoing iq-set
1549 if(state == Target && !task && !proxy_task)
1550 checkForActivation();
1553 void S5BManager::Item::sc_bytesWritten(int)
1555 #ifdef S5B_DEBUG
1556 qDebug("sc_bytesWritten\n");
1557 #endif
1558 // this should only happen to the initiator, and should always be 1 byte (the '\r' sent earlier)
1559 finished();
1562 void S5BManager::Item::sc_error(int)
1564 #ifdef S5B_DEBUG
1565 qDebug("sc_error\n");
1566 #endif
1567 reset();
1568 error(ErrConnect);
1571 void S5BManager::Item::doConnectError()
1573 localFailed = true;
1574 m->doError(peer, in_id, Stanza::Error::RemoteServerNotFound,
1575 "Could not connect to given hosts");
1576 checkFailure();
1579 void S5BManager::Item::tryActivation()
1581 #ifdef S5B_DEBUG
1582 qDebug("tryActivation\n");
1583 #endif
1584 if(activated) {
1585 #ifdef S5B_DEBUG
1586 qDebug("already activated !?\n");
1587 #endif
1588 return;
1591 if(targetMode == NotFast) {
1592 #ifdef S5B_DEBUG
1593 qDebug("tryActivation: NotFast\n");
1594 #endif
1595 // nothing to activate, we're done
1596 finished();
1598 else if(targetMode == Fast) {
1599 // with fast mode, we don't wait for the iq reply, so delete the task (if any)
1600 delete task;
1601 task = 0;
1603 activated = true;
1605 // if udp, activate using special stanza
1606 if(udp) {
1607 m->doActivate(peer, sid, activatedStream);
1609 else {
1610 #ifdef S5B_DEBUG
1611 qDebug("sending extra CR\n");
1612 #endif
1613 // must send [CR] to activate target streamhost
1614 QByteArray a;
1615 a.resize(1);
1616 a[0] = '\r';
1617 client->write(a);
1622 void S5BManager::Item::checkForActivation()
1624 QList<SocksClient*> clientList;
1625 if(client)
1626 clientList.append(client);
1627 if(client_out)
1628 clientList.append(client_out);
1629 foreach(SocksClient *sc, clientList) {
1630 #ifdef S5B_DEBUG
1631 qDebug("checking for activation\n");
1632 #endif
1633 if(fast) {
1634 bool ok = false;
1635 if(udp) {
1636 if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) {
1637 clientList.removeAll(sc);
1638 ok = true;
1641 else {
1642 #ifdef S5B_DEBUG
1643 qDebug("need CR\n");
1644 #endif
1645 if(sc->bytesAvailable() >= 1) {
1646 clientList.removeAll(sc);
1647 QByteArray a = sc->read(1);
1648 if(a[0] != '\r') {
1649 delete sc;
1650 return;
1652 ok = true;
1656 if(ok) {
1657 SocksUDP *sc_udp = 0;
1658 if(sc == client) {
1659 delete client_out_udp;
1660 client_out_udp = 0;
1661 sc_udp = client_udp;
1663 else if(sc == client_out) {
1664 delete client_udp;
1665 client_udp = 0;
1666 sc_udp = client_out_udp;
1669 sc->disconnect(this);
1670 while (!clientList.isEmpty()) {
1671 delete clientList.takeFirst();
1673 client = sc;
1674 client_out = 0;
1675 client_udp = sc_udp;
1676 activated = true;
1677 #ifdef S5B_DEBUG
1678 qDebug("activation success\n");
1679 #endif
1680 break;
1683 else {
1684 #ifdef S5B_DEBUG
1685 qDebug("not fast mode, no need to wait for anything\n");
1686 #endif
1687 clientList.removeAll(sc);
1688 sc->disconnect(this);
1689 while (!clientList.isEmpty()) {
1690 delete clientList.takeFirst();
1692 client = sc;
1693 client_out = 0;
1694 activated = true;
1695 break;
1699 if(activated) {
1700 finished();
1702 else {
1703 // only emit waitingForActivation if there is nothing left to do
1704 if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
1705 waitingForActivation();
1709 void S5BManager::Item::checkFailure()
1711 bool failed = false;
1712 if(state == Initiator) {
1713 if(remoteFailed) {
1714 if((localFailed && targetMode == Fast) || targetMode == NotFast)
1715 failed = true;
1718 else {
1719 if(localFailed) {
1720 if((remoteFailed && fast) || !fast)
1721 failed = true;
1725 if(failed) {
1726 if(state == Initiator) {
1727 reset();
1728 if(statusCode == 404)
1729 error(ErrConnect);
1730 else
1731 error(ErrRefused);
1733 else {
1734 reset();
1735 error(ErrConnect);
1740 void S5BManager::Item::finished()
1742 client->disconnect(this);
1743 state = Active;
1744 #ifdef S5B_DEBUG
1745 qDebug("S5BManager::Item %s [%s] linked successfully\n", qPrintable(peer.full()), qPrintable(sid));
1746 #endif
1747 connected();
1750 //----------------------------------------------------------------------------
1751 // S5BConnector
1752 //----------------------------------------------------------------------------
1753 class S5BConnector::Item : public QObject
1755 Q_OBJECT
1756 public:
1757 SocksClient *client;
1758 SocksUDP *client_udp;
1759 StreamHost host;
1760 QString key;
1761 bool udp;
1762 int udp_tries;
1763 QTimer t;
1764 Jid jid;
1766 Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : QObject(0)
1768 jid = self;
1769 host = _host;
1770 key = _key;
1771 udp = _udp;
1772 client = new SocksClient;
1773 client_udp = 0;
1774 connect(client, SIGNAL(connected()), SLOT(sc_connected()));
1775 connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1776 connect(&t, SIGNAL(timeout()), SLOT(trySendUDP()));
1779 ~Item()
1781 cleanup();
1784 void start()
1786 client->connectToHost(host.host(), host.port(), key, 0, udp);
1789 void udpSuccess()
1791 t.stop();
1792 client_udp->change(key, 0); // flip over to the data port
1793 success();
1796 signals:
1797 void result(bool);
1799 private slots:
1800 void sc_connected()
1802 // if udp, need to send init packet before we are good
1803 if(udp) {
1804 // port 1 is init
1805 client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort());
1806 udp_tries = 0;
1807 t.start(5000);
1808 trySendUDP();
1809 return;
1812 success();
1815 void sc_error(int)
1817 #ifdef S5B_DEBUG
1818 qDebug("S5BConnector[%s]: error\n", qPrintable(host.host()));
1819 #endif
1820 cleanup();
1821 result(false);
1824 void trySendUDP()
1826 if(udp_tries == 5) {
1827 t.stop();
1828 cleanup();
1829 result(false);
1830 return;
1833 // send initialization with our JID
1834 QByteArray a(jid.full().toUtf8());
1835 client_udp->write(a);
1836 ++udp_tries;
1839 private:
1840 void cleanup()
1842 delete client_udp;
1843 client_udp = 0;
1844 delete client;
1845 client = 0;
1848 void success()
1850 #ifdef S5B_DEBUG
1851 qDebug("S5BConnector[%s]: success\n", qPrintable(host.host()));
1852 #endif
1853 client->disconnect(this);
1854 result(true);
1858 class S5BConnector::Private
1860 public:
1861 SocksClient *active;
1862 SocksUDP *active_udp;
1863 QList<Item*> itemList;
1864 QString key;
1865 StreamHost activeHost;
1866 QTimer t;
1869 S5BConnector::S5BConnector(QObject *parent)
1870 :QObject(parent)
1872 d = new Private;
1873 d->active = 0;
1874 d->active_udp = 0;
1875 connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
1878 S5BConnector::~S5BConnector()
1880 reset();
1881 delete d;
1884 void S5BConnector::reset()
1886 d->t.stop();
1887 delete d->active_udp;
1888 d->active_udp = 0;
1889 delete d->active;
1890 d->active = 0;
1891 while (!d->itemList.empty()) {
1892 delete d->itemList.takeFirst();
1896 void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout)
1898 reset();
1900 #ifdef S5B_DEBUG
1901 qDebug("S5BConnector: starting [%p]!\n", this);
1902 #endif
1903 for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1904 Item *i = new Item(self, *it, key, udp);
1905 connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
1906 d->itemList.append(i);
1907 i->start();
1909 d->t.start(timeout * 1000);
1912 SocksClient *S5BConnector::takeClient()
1914 SocksClient *c = d->active;
1915 d->active = 0;
1916 return c;
1919 SocksUDP *S5BConnector::takeUDP()
1921 SocksUDP *c = d->active_udp;
1922 d->active_udp = 0;
1923 return c;
1926 StreamHost S5BConnector::streamHostUsed() const
1928 return d->activeHost;
1931 void S5BConnector::item_result(bool b)
1933 Item *i = (Item *)sender();
1934 if(b) {
1935 d->active = i->client;
1936 i->client = 0;
1937 d->active_udp = i->client_udp;
1938 i->client_udp = 0;
1939 d->activeHost = i->host;
1940 while (!d->itemList.isEmpty()) {
1941 delete d->itemList.takeFirst();
1943 d->t.stop();
1944 #ifdef S5B_DEBUG
1945 qDebug("S5BConnector: complete! [%p]\n", this);
1946 #endif
1947 result(true);
1949 else {
1950 d->itemList.removeAll(i);
1951 delete i;
1952 if(d->itemList.isEmpty()) {
1953 d->t.stop();
1954 #ifdef S5B_DEBUG
1955 qDebug("S5BConnector: failed! [%p]\n", this);
1956 #endif
1957 result(false);
1962 void S5BConnector::t_timeout()
1964 reset();
1965 #ifdef S5B_DEBUG
1966 qDebug("S5BConnector: failed! (timeout)\n");
1967 #endif
1968 result(false);
1971 void S5BConnector::man_udpSuccess(const Jid &streamHost)
1973 // was anyone sending to this streamhost?
1974 foreach(Item *i, d->itemList) {
1975 if(i->host.jid().compare(streamHost) && i->client_udp) {
1976 i->udpSuccess();
1977 return;
1982 //----------------------------------------------------------------------------
1983 // S5BServer
1984 //----------------------------------------------------------------------------
1985 class S5BServer::Item : public QObject
1987 Q_OBJECT
1988 public:
1989 SocksClient *client;
1990 QString host;
1991 QTimer expire;
1993 Item(SocksClient *c) : QObject(0)
1995 client = c;
1996 connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
1997 connect(client, SIGNAL(incomingConnectRequest(const QString &, int)), SLOT(sc_incomingConnectRequest(const QString &, int)));
1998 connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
2000 connect(&expire, SIGNAL(timeout()), SLOT(doError()));
2001 resetExpiration();
2004 ~Item()
2006 delete client;
2009 void resetExpiration()
2011 expire.start(30000);
2014 signals:
2015 void result(bool);
2017 private slots:
2018 void doError()
2020 expire.stop();
2021 delete client;
2022 client = 0;
2023 result(false);
2026 void sc_incomingMethods(int m)
2028 if(m & SocksClient::AuthNone)
2029 client->chooseMethod(SocksClient::AuthNone);
2030 else
2031 doError();
2034 void sc_incomingConnectRequest(const QString &_host, int port)
2036 if(port == 0) {
2037 host = _host;
2038 client->disconnect(this);
2039 result(true);
2041 else
2042 doError();
2045 void sc_error(int)
2047 doError();
2051 class S5BServer::Private
2053 public:
2054 SocksServer serv;
2055 QStringList hostList;
2056 QList<S5BManager*> manList;
2057 QList<Item*> itemList;
2060 S5BServer::S5BServer(QObject *parent)
2061 :QObject(parent)
2063 d = new Private;
2064 connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
2065 connect(&d->serv, SIGNAL(incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)), SLOT(ss_incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)));
2068 S5BServer::~S5BServer()
2070 unlinkAll();
2071 delete d;
2074 bool S5BServer::isActive() const
2076 return d->serv.isActive();
2079 bool S5BServer::start(int port)
2081 d->serv.stop();
2082 //return d->serv.listen(port, true);
2083 return d->serv.listen(port);
2086 void S5BServer::stop()
2088 d->serv.stop();
2091 void S5BServer::setHostList(const QStringList &list)
2093 d->hostList = list;
2096 QStringList S5BServer::hostList() const
2098 return d->hostList;
2101 int S5BServer::port() const
2103 return d->serv.port();
2106 void S5BServer::ss_incomingReady()
2108 Item *i = new Item(d->serv.takeIncoming());
2109 #ifdef S5B_DEBUG
2110 qDebug("S5BServer: incoming connection from %s:%d\n", qPrintable(i->client->peerAddress().toString()), i->client->peerPort());
2111 #endif
2112 connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
2113 d->itemList.append(i);
2116 void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data)
2118 if(port != 0 || port != 1)
2119 return;
2121 foreach(S5BManager* m, d->manList) {
2122 if(m->srv_ownsHash(host)) {
2123 m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data);
2124 return;
2129 void S5BServer::item_result(bool b)
2131 Item *i = (Item *)sender();
2132 #ifdef S5B_DEBUG
2133 qDebug("S5BServer item result: %d\n", b);
2134 #endif
2135 if(!b) {
2136 d->itemList.removeAll(i);
2137 delete i;
2138 return;
2141 SocksClient *c = i->client;
2142 i->client = 0;
2143 QString key = i->host;
2144 d->itemList.removeAll(i);
2145 delete i;
2147 // find the appropriate manager for this incoming connection
2148 foreach(S5BManager *m, d->manList) {
2149 if(m->srv_ownsHash(key)) {
2150 m->srv_incomingReady(c, key);
2151 return;
2155 #ifdef S5B_DEBUG
2156 qDebug("S5BServer item result: unknown hash [%s]\n", qPrintable(key));
2157 #endif
2159 // throw it away
2160 delete c;
2163 void S5BServer::link(S5BManager *m)
2165 d->manList.append(m);
2168 void S5BServer::unlink(S5BManager *m)
2170 d->manList.removeAll(m);
2173 void S5BServer::unlinkAll()
2175 foreach(S5BManager *m, d->manList) {
2176 m->srv_unlink();
2178 d->manList.clear();
2181 const QList<S5BManager*> & S5BServer::managerList() const
2183 return d->manList;
2186 void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
2188 d->serv.writeUDP(addr, port, data);
2191 //----------------------------------------------------------------------------
2192 // JT_S5B
2193 //----------------------------------------------------------------------------
2194 class JT_S5B::Private
2196 public:
2197 QDomElement iq;
2198 Jid to;
2199 Jid streamHost;
2200 StreamHost proxyInfo;
2201 int mode;
2202 QTimer t;
2205 JT_S5B::JT_S5B(Task *parent)
2206 :Task(parent)
2208 d = new Private;
2209 d->mode = -1;
2210 connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
2213 JT_S5B::~JT_S5B()
2215 delete d;
2218 void JT_S5B::request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp)
2220 d->mode = 0;
2222 QDomElement iq;
2223 d->to = to;
2224 iq = createIQ(doc(), "set", to.full(), id());
2225 QDomElement query = doc()->createElement("query");
2226 query.setAttribute("xmlns", S5B_NS);
2227 query.setAttribute("sid", sid);
2228 query.setAttribute("mode", udp ? "udp" : "tcp" );
2229 iq.appendChild(query);
2230 for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
2231 QDomElement shost = doc()->createElement("streamhost");
2232 shost.setAttribute("jid", (*it).jid().full());
2233 shost.setAttribute("host", (*it).host());
2234 shost.setAttribute("port", QString::number((*it).port()));
2235 if((*it).isProxy()) {
2236 QDomElement p = doc()->createElement("proxy");
2237 p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2238 shost.appendChild(p);
2240 query.appendChild(shost);
2242 if(fast) {
2243 QDomElement e = doc()->createElement("fast");
2244 e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2245 query.appendChild(e);
2247 d->iq = iq;
2250 void JT_S5B::requestProxyInfo(const Jid &to)
2252 d->mode = 1;
2254 QDomElement iq;
2255 d->to = to;
2256 iq = createIQ(doc(), "get", to.full(), id());
2257 QDomElement query = doc()->createElement("query");
2258 query.setAttribute("xmlns", S5B_NS);
2259 iq.appendChild(query);
2260 d->iq = iq;
2263 void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
2265 d->mode = 2;
2267 QDomElement iq;
2268 d->to = to;
2269 iq = createIQ(doc(), "set", to.full(), id());
2270 QDomElement query = doc()->createElement("query");
2271 query.setAttribute("xmlns", S5B_NS);
2272 query.setAttribute("sid", sid);
2273 iq.appendChild(query);
2274 QDomElement act = doc()->createElement("activate");
2275 act.appendChild(doc()->createTextNode(target.full()));
2276 query.appendChild(act);
2277 d->iq = iq;
2280 void JT_S5B::onGo()
2282 if(d->mode == 1) {
2283 d->t.setSingleShot(true);
2284 d->t.start(15000);
2286 send(d->iq);
2289 void JT_S5B::onDisconnect()
2291 d->t.stop();
2294 bool JT_S5B::take(const QDomElement &x)
2296 if(d->mode == -1)
2297 return false;
2299 if(!iqVerify(x, d->to, id()))
2300 return false;
2302 d->t.stop();
2304 if(x.attribute("type") == "result") {
2305 QDomElement q = queryTag(x);
2306 if(d->mode == 0) {
2307 d->streamHost = "";
2308 if(!q.isNull()) {
2309 QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
2310 if(!shost.isNull())
2311 d->streamHost = shost.attribute("jid");
2314 setSuccess();
2316 else if(d->mode == 1) {
2317 if(!q.isNull()) {
2318 QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
2319 if(!shost.isNull()) {
2320 Jid j = shost.attribute("jid");
2321 if(j.isValid()) {
2322 QString host = shost.attribute("host");
2323 if(!host.isEmpty()) {
2324 int port = shost.attribute("port").toInt();
2325 StreamHost h;
2326 h.setJid(j);
2327 h.setHost(host);
2328 h.setPort(port);
2329 h.setIsProxy(true);
2330 d->proxyInfo = h;
2336 setSuccess();
2338 else {
2339 setSuccess();
2342 else {
2343 setError(x);
2346 return true;
2349 void JT_S5B::t_timeout()
2351 d->mode = -1;
2352 setError(500, "Timed out");
2355 Jid JT_S5B::streamHostUsed() const
2357 return d->streamHost;
2360 StreamHost JT_S5B::proxyInfo() const
2362 return d->proxyInfo;
2365 //----------------------------------------------------------------------------
2366 // JT_PushS5B
2367 //----------------------------------------------------------------------------
2368 JT_PushS5B::JT_PushS5B(Task *parent)
2369 :Task(parent)
2373 JT_PushS5B::~JT_PushS5B()
2377 int JT_PushS5B::priority() const
2379 return 1;
2382 bool JT_PushS5B::take(const QDomElement &e)
2384 // look for udpsuccess
2385 if(e.tagName() == "message") {
2386 QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement();
2387 if(!x.isNull() && x.attribute("xmlns") == S5B_NS) {
2388 incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr"));
2389 return true;
2391 x = e.elementsByTagName("activate").item(0).toElement();
2392 if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") {
2393 incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid")));
2394 return true;
2396 return false;
2399 // must be an iq-set tag
2400 if(e.tagName() != "iq")
2401 return false;
2402 if(e.attribute("type") != "set")
2403 return false;
2404 if(queryNS(e) != S5B_NS)
2405 return false;
2407 Jid from(e.attribute("from"));
2408 QDomElement q = queryTag(e);
2409 QString sid = q.attribute("sid");
2411 StreamHostList hosts;
2412 QDomNodeList nl = q.elementsByTagName("streamhost");
2413 for(int n = 0; n < nl.count(); ++n) {
2414 QDomElement shost = nl.item(n).toElement();
2415 if(hosts.count() < MAXSTREAMHOSTS) {
2416 Jid j = shost.attribute("jid");
2417 if(!j.isValid())
2418 continue;
2419 QString host = shost.attribute("host");
2420 if(host.isEmpty())
2421 continue;
2422 int port = shost.attribute("port").toInt();
2423 QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
2424 bool isProxy = false;
2425 if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
2426 isProxy = true;
2428 StreamHost h;
2429 h.setJid(j);
2430 h.setHost(host);
2431 h.setPort(port);
2432 h.setIsProxy(isProxy);
2433 hosts += h;
2437 bool fast = false;
2438 QDomElement t;
2439 t = q.elementsByTagName("fast").item(0).toElement();
2440 if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
2441 fast = true;
2443 S5BRequest r;
2444 r.from = from;
2445 r.id = e.attribute("id");
2446 r.sid = sid;
2447 r.hosts = hosts;
2448 r.fast = fast;
2449 r.udp = q.attribute("mode") == "udp" ? true: false;
2451 incoming(r);
2452 return true;
2455 void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
2457 QDomElement iq = createIQ(doc(), "result", to.full(), id);
2458 QDomElement query = doc()->createElement("query");
2459 query.setAttribute("xmlns", S5B_NS);
2460 iq.appendChild(query);
2461 QDomElement shost = doc()->createElement("streamhost-used");
2462 shost.setAttribute("jid", streamHost.full());
2463 query.appendChild(shost);
2464 send(iq);
2467 void JT_PushS5B::respondError(const Jid &to, const QString &id,
2468 Stanza::Error::ErrorCond cond, const QString &str)
2470 QDomElement iq = createIQ(doc(), "error", to.full(), id);
2471 Stanza::Error error(Stanza::Error::Cancel, cond, str);
2472 iq.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS()));
2473 send(iq);
2476 void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr)
2478 QDomElement m = doc()->createElement("message");
2479 m.setAttribute("to", to.full());
2480 QDomElement u = doc()->createElement("udpsuccess");
2481 u.setAttribute("xmlns", S5B_NS);
2482 u.setAttribute("dstaddr", dstaddr);
2483 m.appendChild(u);
2484 send(m);
2487 void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &streamHost)
2489 QDomElement m = doc()->createElement("message");
2490 m.setAttribute("to", to.full());
2491 QDomElement act = doc()->createElement("activate");
2492 act.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2493 act.setAttribute("sid", sid);
2494 act.setAttribute("jid", streamHost.full());
2495 m.appendChild(act);
2496 send(m);
2499 //----------------------------------------------------------------------------
2500 // StreamHost
2501 //----------------------------------------------------------------------------
2502 StreamHost::StreamHost()
2504 v_port = -1;
2505 proxy = false;
2508 const Jid & StreamHost::jid() const
2510 return j;
2513 const QString & StreamHost::host() const
2515 return v_host;
2518 int StreamHost::port() const
2520 return v_port;
2523 bool StreamHost::isProxy() const
2525 return proxy;
2528 void StreamHost::setJid(const Jid &_j)
2530 j = _j;
2533 void StreamHost::setHost(const QString &host)
2535 v_host = host;
2538 void StreamHost::setPort(int port)
2540 v_port = port;
2543 void StreamHost::setIsProxy(bool b)
2545 proxy = b;
2550 #include "s5b.moc"