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
24
#include<qguardedptr.h>
27
#include"xmpp_xmlcommon.h"
30
#define MAXSTREAMHOSTS 5
36
static QString makeKey(const QString &sid, const Jid &initiator, const Jid &target)
38
QString str = sid + initiator.full() + target.full();
39
return QCA::SHA1::hashToString(str.utf8());
42
static bool haveHost(const StreamHostList &list, const Jid &j)
44
for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
45
if((*it).jid().compare(j))
51
class S5BManager::Item : public QObject
55
enum { Idle, Initiator, Target, Active };
56
enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy };
57
enum { Unknown, Fast, NotFast };
60
QString sid, key, out_key, out_id, in_id;
62
StreamHostList in_hosts;
63
JT_S5B *task, *proxy_task;
64
SocksClient *client, *client_out;
65
S5BConnector *conn, *proxy_conn;
68
int targetMode; // initiator sets this once it figures it out
69
bool fast; // target sets this
73
bool localFailed, remoteFailed;
77
Item(S5BManager *manager);
81
void startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast);
82
void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool fast);
83
void handleFast(const StreamHostList &hosts, const QString &iq_id);
87
void setIncomingClient(SocksClient *sc);
91
void tryingHosts(const StreamHostList &list);
93
void waitingForActivation();
99
void conn_result(bool b);
100
void proxy_result(bool b);
101
void proxy_finished();
103
void sc_bytesWritten(int);
107
void doConnectError();
108
void tryActivation();
109
void checkForActivation();
114
//----------------------------------------------------------------------------
116
//----------------------------------------------------------------------------
117
class S5BConnection::Private
128
bool notifyRead, notifyClose;
134
static int id_conn = 0;
135
static int num_conn = 0;
137
S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
147
printf("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
153
S5BConnection::~S5BConnection()
159
printf("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
165
void S5BConnection::reset(bool clear)
167
d->m->con_unlink(this);
178
d->notifyRead = false;
179
d->notifyClose = false;
182
Jid S5BConnection::proxy() const
187
void S5BConnection::setProxy(const Jid &proxy)
192
void S5BConnection::connectToJid(const Jid &peer, const QString &sid)
195
if(!d->m->isAcceptableSID(peer, sid))
200
d->state = Requesting;
202
printf("S5BConnection[%d]: connecting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
204
d->m->con_connect(this);
207
void S5BConnection::accept()
209
if(d->state != WaitingForAccept)
212
d->state = Connecting;
214
printf("S5BConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
216
d->m->con_accept(this);
219
void S5BConnection::close()
224
if(d->state == WaitingForAccept)
225
d->m->con_reject(this);
226
else if(d->state == Active)
229
printf("S5BConnection[%d]: closing %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
234
Jid S5BConnection::peer() const
239
QString S5BConnection::sid() const
244
bool S5BConnection::isRemote() const
249
int S5BConnection::state() const
254
bool S5BConnection::isOpen() const
256
if(d->state == Active)
262
void S5BConnection::write(const QByteArray &buf)
264
if(d->state == Active)
268
QByteArray S5BConnection::read(int bytes)
271
return d->sc->read(bytes);
276
int S5BConnection::bytesAvailable() const
279
return d->sc->bytesAvailable();
284
int S5BConnection::bytesToWrite() const
286
if(d->state == Active)
287
return d->sc->bytesToWrite();
292
void S5BConnection::man_waitForAccept(const S5BRequest &r)
294
d->state = WaitingForAccept;
301
void S5BConnection::man_clientReady(SocksClient *sc)
304
connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
305
connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
306
connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
307
connect(d->sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
308
connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
312
printf("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, d->peer.full().latin1(), d->sid.latin1());
315
// bytes already in the stream?
316
if(d->sc->bytesAvailable()) {
318
printf("Stream has %d bytes in it.\n", d->sc->bytesAvailable());
320
d->notifyRead = true;
322
// closed before it got here?
323
if(!d->sc->isOpen()) {
325
printf("Stream was closed before S5B request finished?\n");
327
d->notifyClose = true;
329
if(d->notifyRead || d->notifyClose)
330
QTimer::singleShot(0, this, SLOT(doPending()));
334
void S5BConnection::doPending()
338
QTimer::singleShot(0, this, SLOT(doPending()));
341
else if(d->notifyClose)
342
sc_connectionClosed();
345
void S5BConnection::man_failed(int x)
348
if(x == S5BManager::Item::ErrRefused)
350
if(x == S5BManager::Item::ErrConnect)
352
if(x == S5BManager::Item::ErrWrongHost)
354
if(x == S5BManager::Item::ErrProxy)
358
void S5BConnection::sc_connectionClosed()
360
// if we have a pending read notification, postpone close
363
printf("closed while pending read\n");
365
d->notifyClose = true;
368
d->notifyClose = false;
373
void S5BConnection::sc_delayedCloseFinished()
376
delayedCloseFinished();
379
void S5BConnection::sc_readyRead()
381
d->notifyRead = false;
386
void S5BConnection::sc_bytesWritten(int x)
392
void S5BConnection::sc_error(int)
398
//----------------------------------------------------------------------------
400
//----------------------------------------------------------------------------
401
class S5BManager::Entry
419
StreamHost proxyInfo;
422
class S5BManager::Private
427
QPtrList<Entry> activeList;
428
S5BConnectionList incomingConns;
432
S5BManager::S5BManager(Client *parent)
438
d->activeList.setAutoDelete(true);
440
d->ps = new JT_PushS5B(d->client->rootTask());
441
connect(d->ps, SIGNAL(incoming(const S5BRequest &)), SLOT(ps_incoming(const S5BRequest &)));
444
S5BManager::~S5BManager()
447
d->incomingConns.setAutoDelete(true);
448
d->incomingConns.clear();
453
Client *S5BManager::client() const
458
S5BServer *S5BManager::server() const
463
void S5BManager::setServer(S5BServer *serv)
466
d->serv->unlink(this);
476
S5BConnection *S5BManager::createConnection()
478
S5BConnection *c = new S5BConnection(this);
482
S5BConnection *S5BManager::takeIncoming()
484
if(d->incomingConns.isEmpty())
487
S5BConnection *c = d->incomingConns.getFirst();
488
d->incomingConns.removeRef(c);
490
// move to activeList
491
Entry *e = new Entry;
494
d->activeList.append(e);
499
void S5BManager::ps_incoming(const S5BRequest &req)
502
printf("S5BManager: incoming from %s\n", req.from.full().latin1());
506
// ensure we don't already have an incoming connection from this peer+sid
507
S5BConnection *c = findIncoming(req.from, req.sid);
509
// do we have an active entry with this sid already?
510
Entry *e = findEntryBySID(req.from, req.sid);
514
if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) {
516
printf("ALLOWED: loopback\n");
520
// allowed by 'fast mode'
521
else if(e->i->state == Item::Initiator && e->i->targetMode == Item::Unknown) {
523
printf("ALLOWED: fast-mode\n");
525
e->i->handleFast(req.hosts, req.id);
532
printf("ALLOWED: we don't have it\n");
538
d->ps->respondError(req.from, req.id, 406, "SID in use");
542
// create an incoming connection
543
c = new S5BConnection(this);
544
c->man_waitForAccept(req);
545
d->incomingConns.append(c);
549
void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
551
d->ps->respondSuccess(peer, id, streamHost);
554
void S5BManager::doError(const Jid &peer, const QString &id, int code, const QString &str)
556
d->ps->respondError(peer, id, code, str);
559
QString S5BManager::genUniqueSID(const Jid &peer) const
565
for(int i = 0; i < 4; ++i) {
566
int word = rand() & 0xffff;
567
for(int n = 0; n < 4; ++n) {
569
s.sprintf("%x", (word >> (n * 4)) & 0xf);
573
} while(!isAcceptableSID(peer, sid));
577
bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
579
QString key = makeKey(sid, d->client->jid(), peer);
580
QString key_out = makeKey(sid, peer, d->client->jid());
582
// if we have a server, then check through it
584
if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
588
if(findEntryByHash(key) || findEntryByHash(key_out))
594
S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
596
QPtrListIterator<S5BConnection> it(d->incomingConns);
597
for(S5BConnection *c; (c = it.current()); ++it) {
598
if(c->d->peer.compare(from) && c->d->sid == sid)
604
S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
606
QPtrListIterator<Entry> it(d->activeList);
607
for(Entry *e; (e = it.current()); ++it) {
614
S5BManager::Entry *S5BManager::findEntry(Item *i) const
616
QPtrListIterator<Entry> it(d->activeList);
617
for(Entry *e; (e = it.current()); ++it) {
624
S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
626
QPtrListIterator<Entry> it(d->activeList);
627
for(Entry *e; (e = it.current()); ++it) {
628
if(e->i && e->i->key == key)
634
S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
636
QPtrListIterator<Entry> it(d->activeList);
637
for(Entry *e; (e = it.current()); ++it) {
638
if(e->i && e->i->peer.compare(peer) && e->sid == sid)
644
S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
646
const QPtrList<S5BManager> &manList = d->serv->managerList();
647
QPtrListIterator<S5BManager> it(manList);
648
for(S5BManager *m; (m = it.current()); ++it) {
649
Entry *e = m->findEntryByHash(key);
656
bool S5BManager::srv_ownsHash(const QString &key) const
658
if(findEntryByHash(key))
663
void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
665
Entry *e = findEntryByHash(key);
666
if(!e->i->allowIncoming) {
667
sc->requestGrant(false);
671
sc->requestGrant(true);
672
e->i->setIncomingClient(sc);
675
void S5BManager::srv_unlink()
680
void S5BManager::con_connect(S5BConnection *c)
684
Entry *e = new Entry;
687
d->activeList.append(e);
689
if(c->d->proxy.isValid()) {
696
void S5BManager::con_accept(S5BConnection *c)
698
Entry *e = findEntry(c);
702
if(e->c->d->req.fast) {
703
if(targetShouldOfferProxy(e)) {
711
void S5BManager::con_reject(S5BConnection *c)
713
d->ps->respondError(c->d->peer, c->d->req.id, 406, "Not acceptable");
716
void S5BManager::con_unlink(S5BConnection *c)
718
Entry *e = findEntry(c);
722
// active incoming request? cancel it
723
if(e->i && e->i->conn)
724
d->ps->respondError(e->i->peer, e->i->out_id, 406, "Not acceptable");
726
d->activeList.removeRef(e);
729
void S5BManager::item_accepted()
731
Item *i = (Item *)sender();
732
Entry *e = findEntry(i);
734
e->c->accepted(); // signal
737
void S5BManager::item_tryingHosts(const StreamHostList &list)
739
Item *i = (Item *)sender();
740
Entry *e = findEntry(i);
742
e->c->tryingHosts(list); // signal
745
void S5BManager::item_proxyConnect()
747
Item *i = (Item *)sender();
748
Entry *e = findEntry(i);
750
e->c->proxyConnect(); // signal
753
void S5BManager::item_waitingForActivation()
755
Item *i = (Item *)sender();
756
Entry *e = findEntry(i);
758
e->c->waitingForActivation(); // signal
761
void S5BManager::item_connected()
763
Item *i = (Item *)sender();
764
Entry *e = findEntry(i);
767
SocksClient *client = i->client;
770
// give it to the connection
771
e->c->man_clientReady(client);
774
void S5BManager::item_error(int x)
776
Item *i = (Item *)sender();
777
Entry *e = findEntry(i);
782
void S5BManager::entryContinue(Entry *e)
784
e->i = new Item(this);
785
e->i->proxy = e->proxyInfo;
787
connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
788
connect(e->i, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(item_tryingHosts(const StreamHostList &)));
789
connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
790
connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
791
connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
792
connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
794
if(e->c->isRemote()) {
795
const S5BRequest &req = e->c->d->req;
796
e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.hosts, req.id, req.fast);
799
e->i->startInitiator(e->sid, d->client->jid(), e->c->d->peer, true);
800
e->c->requesting(); // signal
804
void S5BManager::queryProxy(Entry *e)
806
QGuardedPtr<QObject> self = this;
807
e->c->proxyQuery(); // signal
812
printf("querying proxy: [%s]\n", e->c->d->proxy.full().latin1());
814
e->query = new JT_S5B(d->client->rootTask());
815
connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
816
e->query->requestProxyInfo(e->c->d->proxy);
820
void S5BManager::query_finished()
822
JT_S5B *query = (JT_S5B *)sender();
825
QPtrListIterator<Entry> it(d->activeList);
826
for(; (e = it.current()); ++it) {
827
if(e->query == query) {
837
printf("query finished: ");
839
if(query->success()) {
840
e->proxyInfo = query->proxyInfo();
842
printf("host/ip=[%s] port=[%d]\n", e->proxyInfo.host().latin1(), e->proxyInfo.port());
851
QGuardedPtr<QObject> self = this;
852
e->c->proxyResult(query->success()); // signal
859
bool S5BManager::targetShouldOfferProxy(Entry *e)
861
if(!e->c->d->proxy.isValid())
864
// if target, don't offer any proxy if the initiator already did
865
const StreamHostList &hosts = e->c->d->req.hosts;
866
for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
871
// ensure we don't offer the same proxy as the initiator
872
if(haveHost(hosts, e->c->d->proxy))
878
//----------------------------------------------------------------------------
880
//----------------------------------------------------------------------------
881
S5BManager::Item::Item(S5BManager *manager) : QObject(0)
893
S5BManager::Item::~Item()
898
void S5BManager::Item::reset()
920
targetMode = Unknown;
926
remoteFailed = false;
927
allowIncoming = false;
930
void S5BManager::Item::startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast)
935
key = makeKey(sid, self, peer);
936
out_key = makeKey(sid, peer, self);
940
printf("S5BManager::Item initiating request %s [%s]\n", peer.full().latin1(), sid.latin1());
946
void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool _fast)
954
key = makeKey(sid, self, peer);
955
out_key = makeKey(sid, peer, self);
958
printf("S5BManager::Item incoming request %s [%s]\n", peer.full().latin1(), sid.latin1());
966
void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
970
QGuardedPtr<QObject> self = this;
975
// if we already have a stream, then bounce this request
977
m->doError(peer, iq_id, 406, "Not acceptable");
986
void S5BManager::Item::doOutgoing()
988
StreamHostList hosts;
989
S5BServer *serv = m->server();
990
if(serv && serv->isActive() && !haveHost(in_hosts, m->client()->jid())) {
991
QStringList hostList = serv->hostList();
992
for(QStringList::ConstIterator it = hostList.begin(); it != hostList.end(); ++it) {
994
h.setJid(m->client()->jid());
996
h.setPort(serv->port());
1001
// if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
1002
if(proxy.jid().isValid())
1005
// if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
1006
if(state == Target && hosts.isEmpty()) {
1011
allowIncoming = true;
1013
task = new JT_S5B(m->client()->rootTask());
1014
connect(task, SIGNAL(finished()), SLOT(jt_finished()));
1015
task->request(peer, sid, hosts, state == Initiator ? wantFast : false);
1016
out_id = task->id();
1020
void S5BManager::Item::doIncoming()
1022
if(in_hosts.isEmpty()) {
1027
StreamHostList list;
1029
// take just the proxy streamhosts
1030
for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
1037
// only try doing the late proxy trick if using fast mode AND we did not offer a proxy
1038
if((state == Initiator || (state == Target && fast)) && !proxy.jid().isValid()) {
1039
// take just the non-proxy streamhosts
1040
bool hasProxies = false;
1041
for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
1050
// no regular streamhosts? wait for remote error
1059
conn = new S5BConnector;
1060
connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
1062
QGuardedPtr<QObject> self = this;
1067
conn->start(list, out_key, lateProxy ? 10 : 30);
1070
void S5BManager::Item::setIncomingClient(SocksClient *sc)
1073
printf("S5BManager::Item: %s [%s] successful incoming connection\n", peer.full().latin1(), sid.latin1());
1076
connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1077
connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1078
connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1081
allowIncoming = false;
1084
void S5BManager::Item::jt_finished()
1090
printf("jt_finished: state=%s, %s\n", state == Initiator ? "initiator" : "target", j->success() ? "ok" : "fail");
1093
if(state == Initiator) {
1094
if(targetMode == Unknown) {
1095
targetMode = NotFast;
1096
QGuardedPtr<QObject> self = this;
1103
// if we've already reported successfully connecting to them, then this response doesn't matter
1104
if(state == Initiator && connSuccess) {
1110
// stop connecting out
1111
if(conn || lateProxy) {
1117
Jid streamHost = j->streamHostUsed();
1118
// they connected to us?
1119
if(streamHost.compare(self)) {
1121
if(state == Initiator)
1124
checkForActivation();
1128
printf("S5BManager::Item %s claims to have connected to us, but we don't see this\n", peer.full().latin1());
1131
error(ErrWrongHost);
1134
else if(streamHost.compare(proxy.jid())) {
1135
// toss out any direct incoming, since it won't be used
1138
allowIncoming = false;
1141
printf("attempting to connect to proxy\n");
1143
// connect to the proxy
1144
proxy_conn = new S5BConnector;
1145
connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
1146
StreamHostList list;
1149
QGuardedPtr<QObject> self = this;
1154
proxy_conn->start(list, key, 30);
1158
printf("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", peer.full().latin1());
1161
error(ErrWrongHost);
1166
printf("S5BManager::Item %s [%s] error\n", peer.full().latin1(), sid.latin1());
1168
remoteFailed = true;
1169
statusCode = j->statusCode();
1176
// if connSuccess is true at this point, then we're a Target
1178
checkForActivation();
1185
void S5BManager::Item::conn_result(bool b)
1188
SocksClient *sc = conn->takeClient();
1189
StreamHost h = conn->streamHostUsed();
1195
printf("S5BManager::Item: %s [%s] successful outgoing connection\n", peer.full().latin1(), sid.latin1());
1198
connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1199
connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1200
connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1202
m->doSuccess(peer, in_id, h.jid());
1204
// if the first batch works, don't try proxy
1207
// if initiator, run with this one
1208
if(state == Initiator) {
1209
// if we had an incoming one, toss it
1212
allowIncoming = false;
1217
checkForActivation();
1224
// if we delayed the proxies for later, try now
1234
void S5BManager::Item::proxy_result(bool b)
1237
printf("proxy_result: %s\n", b ? "ok" : "fail");
1240
SocksClient *sc = proxy_conn->takeClient();
1244
connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1245
connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1246
connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1252
printf("activating proxy stream\n");
1254
proxy_task = new JT_S5B(m->client()->rootTask());
1255
connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
1256
proxy_task->requestActivation(proxy.jid(), sid, peer);
1257
proxy_task->go(true);
1267
void S5BManager::Item::proxy_finished()
1269
JT_S5B *j = proxy_task;
1274
printf("proxy stream activated\n");
1276
if(state == Initiator)
1279
checkForActivation();
1287
void S5BManager::Item::sc_readyRead()
1290
printf("sc_readyRead\n");
1292
// only targets check for activation, and only should do it if there is no pending outgoing iq-set
1293
if(state == Target && !task && !proxy_task)
1294
checkForActivation();
1297
void S5BManager::Item::sc_bytesWritten(int)
1300
printf("sc_bytesWritten\n");
1302
// this should only happen to the initiator, and should always be 1 byte (the '\r' sent earlier)
1306
void S5BManager::Item::sc_error(int)
1309
printf("sc_error\n");
1315
void S5BManager::Item::doConnectError()
1318
m->doError(peer, in_id, 404, "Could not connect to given hosts");
1322
void S5BManager::Item::tryActivation()
1325
printf("tryActivation\n");
1329
printf("already activated !?\n");
1334
if(targetMode == NotFast) {
1336
printf("tryActivation: NotFast\n");
1338
// nothing to activate, we're done
1341
else if(targetMode == Fast) {
1342
// with fast mode, we don't wait for the iq reply, so delete the task (if any)
1347
printf("sending extra CR\n");
1349
// must send [CR] to activate target streamhost
1357
void S5BManager::Item::checkForActivation()
1359
QPtrList<SocksClient> clientList;
1361
clientList.append(client);
1363
clientList.append(client_out);
1364
QPtrListIterator<SocksClient> it(clientList);
1365
for(SocksClient *sc; (sc = it.current()); ++it) {
1367
printf("checking for activation\n");
1371
printf("need CR\n");
1373
if(sc->bytesAvailable() >= 1) {
1374
clientList.removeRef(sc);
1375
QByteArray a = sc->read(1);
1380
sc->disconnect(this);
1381
clientList.setAutoDelete(true);
1387
printf("activation success\n");
1394
printf("not fast mode, no need to wait for anything\n");
1396
clientList.removeRef(sc);
1397
sc->disconnect(this);
1398
clientList.setAutoDelete(true);
1411
// only emit waitingForActivation if there is nothing left to do
1412
if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
1413
waitingForActivation();
1417
void S5BManager::Item::checkFailure()
1419
bool failed = false;
1420
if(state == Initiator) {
1422
if((localFailed && targetMode == Fast) || targetMode == NotFast)
1428
if((remoteFailed && fast) || !fast)
1434
if(state == Initiator) {
1436
if(statusCode == 404)
1448
void S5BManager::Item::finished()
1450
client->disconnect(this);
1453
printf("S5BManager::Item %s [%s] linked successfully\n", peer.full().latin1(), sid.latin1());
1458
//----------------------------------------------------------------------------
1460
//----------------------------------------------------------------------------
1461
class S5BConnector::Item : public QObject
1465
SocksClient *client;
1469
Item(const StreamHost &_host, const QString &_key) : QObject(0)
1473
client = new SocksClient;
1474
connect(client, SIGNAL(connected()), SLOT(sc_connected()));
1475
connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1485
client->connectToHost(host.host(), host.port(), key, 0);
1495
printf("S5BConnector[%s]: success\n", host.host().latin1());
1497
client->disconnect(this);
1504
printf("S5BConnector[%s]: error\n", host.host().latin1());
1512
class S5BConnector::Private
1515
SocksClient *active;
1516
QPtrList<Item> itemList;
1518
StreamHost activeHost;
1522
S5BConnector::S5BConnector(QObject *parent)
1527
d->itemList.setAutoDelete(true);
1528
connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
1531
S5BConnector::~S5BConnector()
1537
void S5BConnector::reset()
1542
d->itemList.clear();
1545
void S5BConnector::start(const StreamHostList &hosts, const QString &key, int timeout)
1550
printf("S5BConnector: starting [%p]!\n", this);
1552
for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1553
Item *i = new Item(*it, key);
1554
connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
1555
d->itemList.append(i);
1558
d->t.start(timeout * 1000);
1561
SocksClient *S5BConnector::takeClient()
1563
SocksClient *c = d->active;
1568
StreamHost S5BConnector::streamHostUsed() const
1570
return d->activeHost;
1573
void S5BConnector::item_result(bool b)
1575
Item *i = (Item *)sender();
1577
d->active = i->client;
1579
d->activeHost = i->host;
1580
d->itemList.clear();
1583
printf("S5BConnector: complete! [%p]\n", this);
1588
d->itemList.removeRef(i);
1589
if(d->itemList.isEmpty()) {
1592
printf("S5BConnector: failed! [%p]\n", this);
1599
void S5BConnector::t_timeout()
1603
printf("S5BConnector: failed! (timeout)\n");
1608
//----------------------------------------------------------------------------
1610
//----------------------------------------------------------------------------
1611
class S5BServer::Item : public QObject
1615
SocksClient *client;
1619
Item(SocksClient *c) : QObject(0)
1622
connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
1623
connect(client, SIGNAL(incomingRequest(const QString &, int)), SLOT(sc_incomingRequest(const QString &, int)));
1624
connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1626
connect(&expire, SIGNAL(timeout()), SLOT(doError()));
1635
void resetExpiration()
1637
expire.start(30000);
1652
void sc_incomingMethods(int m)
1654
if(m & SocksClient::AuthNone)
1655
client->chooseMethod(SocksClient::AuthNone);
1660
void sc_incomingRequest(const QString &_host, int port)
1664
client->disconnect(this);
1677
class S5BServer::Private
1681
QStringList hostList;
1682
QPtrList<S5BManager> manList;
1683
QPtrList<Item> itemList;
1686
S5BServer::S5BServer(QObject *parent)
1690
d->itemList.setAutoDelete(true);
1691
connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
1694
S5BServer::~S5BServer()
1700
bool S5BServer::isActive() const
1702
return d->serv.isActive();
1705
bool S5BServer::start(int port)
1708
return d->serv.listen(port);
1711
void S5BServer::stop()
1716
void S5BServer::setHostList(const QStringList &list)
1721
QStringList S5BServer::hostList() const
1726
int S5BServer::port() const
1728
return d->serv.port();
1731
void S5BServer::ss_incomingReady()
1733
Item *i = new Item(d->serv.takeIncoming());
1735
printf("S5BServer: incoming connection from %s:%d\n", i->client->peerAddress().toString().latin1(), i->client->peerPort());
1737
connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
1738
d->itemList.append(i);
1741
void S5BServer::item_result(bool b)
1743
Item *i = (Item *)sender();
1745
printf("S5BServer item result: %d\n", b);
1748
d->itemList.removeRef(i);
1752
SocksClient *c = i->client;
1754
QString key = i->host;
1755
d->itemList.removeRef(i);
1757
// find the appropriate manager for this incoming connection
1758
QPtrListIterator<S5BManager> it(d->manList);
1759
for(S5BManager *m; (m = it.current()); ++it) {
1760
if(m->srv_ownsHash(key)) {
1761
m->srv_incomingReady(c, key);
1770
void S5BServer::link(S5BManager *m)
1772
d->manList.append(m);
1775
void S5BServer::unlink(S5BManager *m)
1777
d->manList.removeRef(m);
1780
void S5BServer::unlinkAll()
1782
QPtrListIterator<S5BManager> it(d->manList);
1783
for(S5BManager *m; (m = it.current()); ++it)
1788
const QPtrList<S5BManager> & S5BServer::managerList() const
1793
//----------------------------------------------------------------------------
1795
//----------------------------------------------------------------------------
1796
class JT_S5B::Private
1802
StreamHost proxyInfo;
1807
JT_S5B::JT_S5B(Task *parent)
1812
connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
1820
void JT_S5B::request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast)
1826
iq = createIQ(doc(), "set", to.full(), id());
1827
QDomElement query = doc()->createElement("query");
1828
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
1829
query.setAttribute("sid", sid);
1830
iq.appendChild(query);
1831
for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1832
QDomElement shost = doc()->createElement("streamhost");
1833
shost.setAttribute("jid", (*it).jid().full());
1834
shost.setAttribute("host", (*it).host());
1835
shost.setAttribute("port", QString::number((*it).port()));
1836
if((*it).isProxy()) {
1837
QDomElement p = doc()->createElement("proxy");
1838
p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
1839
shost.appendChild(p);
1841
query.appendChild(shost);
1844
QDomElement e = doc()->createElement("fast");
1845
e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
1846
query.appendChild(e);
1851
void JT_S5B::requestProxyInfo(const Jid &to)
1857
iq = createIQ(doc(), "get", to.full(), id());
1858
QDomElement query = doc()->createElement("query");
1859
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
1860
iq.appendChild(query);
1864
void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
1870
iq = createIQ(doc(), "set", to.full(), id());
1871
QDomElement query = doc()->createElement("query");
1872
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
1873
query.setAttribute("sid", sid);
1874
iq.appendChild(query);
1875
QDomElement act = doc()->createElement("activate");
1876
act.appendChild(doc()->createTextNode(target.full()));
1877
query.appendChild(act);
1884
d->t.start(15000, true);
1888
void JT_S5B::onDisconnect()
1893
bool JT_S5B::take(const QDomElement &x)
1898
if(!iqVerify(x, d->to, id()))
1903
if(x.attribute("type") == "result") {
1904
QDomElement q = queryTag(x);
1908
QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
1910
d->streamHost = shost.attribute("jid");
1915
else if(d->mode == 1) {
1917
QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
1918
if(!shost.isNull()) {
1919
Jid j = shost.attribute("jid");
1921
QString host = shost.attribute("host");
1922
if(!host.isEmpty()) {
1923
int port = shost.attribute("port").toInt();
1948
void JT_S5B::t_timeout()
1951
setError(500, "Timed out");
1954
Jid JT_S5B::streamHostUsed() const
1956
return d->streamHost;
1959
StreamHost JT_S5B::proxyInfo() const
1961
return d->proxyInfo;
1964
//----------------------------------------------------------------------------
1966
//----------------------------------------------------------------------------
1967
JT_PushS5B::JT_PushS5B(Task *parent)
1972
JT_PushS5B::~JT_PushS5B()
1976
bool JT_PushS5B::take(const QDomElement &e)
1978
// must be an iq-set tag
1979
if(e.tagName() != "iq")
1981
if(e.attribute("type") != "set")
1983
if(queryNS(e) != "http://jabber.org/protocol/bytestreams")
1986
Jid from(e.attribute("from"));
1987
QDomElement q = queryTag(e);
1988
QString sid = q.attribute("sid");
1990
StreamHostList hosts;
1991
QDomNodeList nl = q.elementsByTagName("streamhost");
1992
for(uint n = 0; n < nl.count(); ++n) {
1993
QDomElement shost = nl.item(n).toElement();
1994
if(hosts.count() < MAXSTREAMHOSTS) {
1995
Jid j = shost.attribute("jid");
1998
QString host = shost.attribute("host");
2001
int port = shost.attribute("port").toInt();
2002
QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
2003
bool isProxy = false;
2004
if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
2011
h.setIsProxy(isProxy);
2018
t = q.elementsByTagName("fast").item(0).toElement();
2019
if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
2024
r.id = e.attribute("id");
2033
void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
2035
QDomElement iq = createIQ(doc(), "result", to.full(), id);
2036
QDomElement query = doc()->createElement("query");
2037
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
2038
iq.appendChild(query);
2039
QDomElement shost = doc()->createElement("streamhost-used");
2040
shost.setAttribute("jid", streamHost.full());
2041
query.appendChild(shost);
2045
void JT_PushS5B::respondError(const Jid &to, const QString &id, int code, const QString &str)
2047
QDomElement iq = createIQ(doc(), "error", to.full(), id);
2048
QDomElement err = textTag(doc(), "error", str);
2049
err.setAttribute("code", QString::number(code));
2050
iq.appendChild(err);
2054
//----------------------------------------------------------------------------
2056
//----------------------------------------------------------------------------
2057
StreamHost::StreamHost()
2063
const Jid & StreamHost::jid() const
2068
const QString & StreamHost::host() const
2073
int StreamHost::port() const
2078
bool StreamHost::isProxy() const
2083
void StreamHost::setJid(const Jid &_j)
2088
void StreamHost::setHost(const QString &host)
2093
void StreamHost::setPort(int port)
2098
void StreamHost::setIsProxy(bool b)