source: psi/trunk/iris/jabber/s5b.cpp@ 151

Last change on this file since 151 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

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