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"
29
#include"safedelete.h"
34
# include <netinet/in.h>
37
#define MAXSTREAMHOSTS 5
43
static QString makeKey(const QString &sid, const Jid &initiator, const Jid &target)
45
QString str = sid + initiator.full() + target.full();
46
return QCA::SHA1::hashToString(str.utf8());
49
static bool haveHost(const StreamHostList &list, const Jid &j)
51
for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
52
if((*it).jid().compare(j))
58
class S5BManager::Item : public QObject
62
enum { Idle, Initiator, Target, Active };
63
enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy };
64
enum { Unknown, Fast, NotFast };
67
QString sid, key, out_key, out_id, in_id;
69
StreamHostList in_hosts;
70
JT_S5B *task, *proxy_task;
71
SocksClient *client, *client_out;
72
SocksUDP *client_udp, *client_out_udp;
73
S5BConnector *conn, *proxy_conn;
76
int targetMode; // initiator sets this once it figures it out
77
bool fast; // target sets this
81
bool localFailed, remoteFailed;
87
Item(S5BManager *manager);
91
void startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool udp);
92
void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool fast, bool udp);
93
void handleFast(const StreamHostList &hosts, const QString &iq_id);
97
void setIncomingClient(SocksClient *sc);
98
void incomingActivate(const Jid &streamHost);
102
void tryingHosts(const StreamHostList &list);
104
void waitingForActivation();
110
void conn_result(bool b);
111
void proxy_result(bool b);
112
void proxy_finished();
114
void sc_bytesWritten(int);
118
void doConnectError();
119
void tryActivation();
120
void checkForActivation();
125
//----------------------------------------------------------------------------
127
//----------------------------------------------------------------------------
128
S5BDatagram::S5BDatagram()
134
S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data)
141
int S5BDatagram::sourcePort() const
146
int S5BDatagram::destPort() const
151
QByteArray S5BDatagram::data() const
156
//----------------------------------------------------------------------------
158
//----------------------------------------------------------------------------
159
class S5BConnection::Private
170
bool notifyRead, notifyClose;
175
QPtrList<S5BDatagram> dglist;
178
static int id_conn = 0;
179
static int num_conn = 0;
181
S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
192
printf("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
198
S5BConnection::~S5BConnection()
204
printf("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
210
void S5BConnection::reset(bool clear)
212
d->m->con_unlink(this);
220
d->dglist.setAutoDelete(true);
222
d->dglist.setAutoDelete(false);
229
d->notifyRead = false;
230
d->notifyClose = false;
233
Jid S5BConnection::proxy() const
238
void S5BConnection::setProxy(const Jid &proxy)
243
void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m)
246
if(!d->m->isAcceptableSID(peer, sid))
251
d->state = Requesting;
254
printf("S5BConnection[%d]: connecting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
256
d->m->con_connect(this);
259
void S5BConnection::accept()
261
if(d->state != WaitingForAccept)
264
d->state = Connecting;
266
printf("S5BConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
268
d->m->con_accept(this);
271
void S5BConnection::close()
276
if(d->state == WaitingForAccept)
277
d->m->con_reject(this);
278
else if(d->state == Active)
281
printf("S5BConnection[%d]: closing %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
286
Jid S5BConnection::peer() const
291
QString S5BConnection::sid() const
296
bool S5BConnection::isRemote() const
301
S5BConnection::Mode S5BConnection::mode() const
306
int S5BConnection::state() const
311
bool S5BConnection::isOpen() const
313
if(d->state == Active)
319
void S5BConnection::write(const QByteArray &buf)
321
if(d->state == Active && d->mode == Stream)
325
QByteArray S5BConnection::read(int bytes)
328
return d->sc->read(bytes);
333
int S5BConnection::bytesAvailable() const
336
return d->sc->bytesAvailable();
341
int S5BConnection::bytesToWrite() const
343
if(d->state == Active)
344
return d->sc->bytesToWrite();
349
void S5BConnection::writeDatagram(const S5BDatagram &i)
351
QByteArray buf(i.data().size() + 4);
352
ushort ssp = htons(i.sourcePort());
353
ushort sdp = htons(i.destPort());
354
QByteArray data = i.data();
355
memcpy(buf.data(), &ssp, 2);
356
memcpy(buf.data() + 2, &sdp, 2);
357
memcpy(buf.data() + 4, data.data(), data.size());
361
S5BDatagram S5BConnection::readDatagram()
363
if(d->dglist.isEmpty())
364
return S5BDatagram();
365
S5BDatagram *i = d->dglist.getFirst();
366
d->dglist.removeRef(i);
367
S5BDatagram val = *i;
372
int S5BConnection::datagramsAvailable() const
374
return d->dglist.count();
377
void S5BConnection::man_waitForAccept(const S5BRequest &r)
379
d->state = WaitingForAccept;
384
d->mode = r.udp ? Datagram : Stream;
387
void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp)
390
connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
391
connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
392
connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
393
connect(d->sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
394
connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
398
connect(d->su, SIGNAL(packetReady(const QByteArray &)), SLOT(su_packetReady(const QByteArray &)));
403
printf("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, d->peer.full().latin1(), d->sid.latin1());
406
// bytes already in the stream?
407
if(d->sc->bytesAvailable()) {
409
printf("Stream has %d bytes in it.\n", d->sc->bytesAvailable());
411
d->notifyRead = true;
413
// closed before it got here?
414
if(!d->sc->isOpen()) {
416
printf("Stream was closed before S5B request finished?\n");
418
d->notifyClose = true;
420
if(d->notifyRead || d->notifyClose)
421
QTimer::singleShot(0, this, SLOT(doPending()));
425
void S5BConnection::doPending()
429
QTimer::singleShot(0, this, SLOT(doPending()));
432
else if(d->notifyClose)
433
sc_connectionClosed();
436
void S5BConnection::man_udpReady(const QByteArray &buf)
441
void S5BConnection::man_failed(int x)
444
if(x == S5BManager::Item::ErrRefused)
446
if(x == S5BManager::Item::ErrConnect)
448
if(x == S5BManager::Item::ErrWrongHost)
450
if(x == S5BManager::Item::ErrProxy)
454
void S5BConnection::sc_connectionClosed()
456
// if we have a pending read notification, postpone close
459
printf("closed while pending read\n");
461
d->notifyClose = true;
464
d->notifyClose = false;
469
void S5BConnection::sc_delayedCloseFinished()
472
delayedCloseFinished();
475
void S5BConnection::sc_readyRead()
477
if(d->mode == Datagram) {
478
// throw the data away
483
d->notifyRead = false;
488
void S5BConnection::sc_bytesWritten(int x)
494
void S5BConnection::sc_error(int)
500
void S5BConnection::su_packetReady(const QByteArray &buf)
505
void S5BConnection::handleUDP(const QByteArray &buf)
507
// must be at least 4 bytes, to accomodate virtual ports
512
memcpy(&ssp, buf.data(), 2);
513
memcpy(&sdp, buf.data() + 2, 2);
514
int source = ntohs(ssp);
515
int dest = ntohs(sdp);
516
QByteArray data(buf.size() - 4);
517
memcpy(data.data(), buf.data() + 4, data.size());
518
d->dglist.append(new S5BDatagram(source, dest, data));
523
void S5BConnection::sendUDP(const QByteArray &buf)
528
d->m->con_sendUDP(this, buf);
531
//----------------------------------------------------------------------------
533
//----------------------------------------------------------------------------
534
class S5BManager::Entry
553
StreamHost proxyInfo;
554
QGuardedPtr<S5BServer> relatedServer;
557
QHostAddress udp_addr;
561
class S5BManager::Private
566
QPtrList<Entry> activeList;
567
S5BConnectionList incomingConns;
571
S5BManager::S5BManager(Client *parent)
577
d->activeList.setAutoDelete(true);
579
d->ps = new JT_PushS5B(d->client->rootTask());
580
connect(d->ps, SIGNAL(incoming(const S5BRequest &)), SLOT(ps_incoming(const S5BRequest &)));
581
connect(d->ps, SIGNAL(incomingUDPSuccess(const Jid &, const QString &)), SLOT(ps_incomingUDPSuccess(const Jid &, const QString &)));
582
connect(d->ps, SIGNAL(incomingActivate(const Jid &, const QString &, const Jid &)), SLOT(ps_incomingActivate(const Jid &, const QString &, const Jid &)));
585
S5BManager::~S5BManager()
588
d->incomingConns.setAutoDelete(true);
589
d->incomingConns.clear();
594
Client *S5BManager::client() const
599
S5BServer *S5BManager::server() const
604
void S5BManager::setServer(S5BServer *serv)
607
d->serv->unlink(this);
617
S5BConnection *S5BManager::createConnection()
619
S5BConnection *c = new S5BConnection(this);
623
S5BConnection *S5BManager::takeIncoming()
625
if(d->incomingConns.isEmpty())
628
S5BConnection *c = d->incomingConns.getFirst();
629
d->incomingConns.removeRef(c);
631
// move to activeList
632
Entry *e = new Entry;
635
d->activeList.append(e);
640
void S5BManager::ps_incoming(const S5BRequest &req)
643
printf("S5BManager: incoming from %s\n", req.from.full().latin1());
647
// ensure we don't already have an incoming connection from this peer+sid
648
S5BConnection *c = findIncoming(req.from, req.sid);
650
// do we have an active entry with this sid already?
651
Entry *e = findEntryBySID(req.from, req.sid);
655
if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) {
657
printf("ALLOWED: loopback\n");
661
// allowed by 'fast mode'
662
else if(e->i->state == Item::Initiator && e->i->targetMode == Item::Unknown) {
664
printf("ALLOWED: fast-mode\n");
666
e->i->handleFast(req.hosts, req.id);
673
printf("ALLOWED: we don't have it\n");
679
d->ps->respondError(req.from, req.id, 406, "SID in use");
683
// create an incoming connection
684
c = new S5BConnection(this);
685
c->man_waitForAccept(req);
686
d->incomingConns.append(c);
690
void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key)
692
Entry *e = findEntryByHash(key);
695
e->i->conn->man_udpSuccess(from);
696
else if(e->i->proxy_conn)
697
e->i->proxy_conn->man_udpSuccess(from);
701
void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost)
703
Entry *e = findEntryBySID(from, sid);
705
e->i->incomingActivate(streamHost);
708
void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
710
d->ps->respondSuccess(peer, id, streamHost);
713
void S5BManager::doError(const Jid &peer, const QString &id, int code, const QString &str)
715
d->ps->respondError(peer, id, code, str);
718
void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &streamHost)
720
d->ps->sendActivate(peer, sid, streamHost);
723
QString S5BManager::genUniqueSID(const Jid &peer) const
729
for(int i = 0; i < 4; ++i) {
730
int word = rand() & 0xffff;
731
for(int n = 0; n < 4; ++n) {
733
s.sprintf("%x", (word >> (n * 4)) & 0xf);
737
} while(!isAcceptableSID(peer, sid));
741
bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
743
QString key = makeKey(sid, d->client->jid(), peer);
744
QString key_out = makeKey(sid, peer, d->client->jid());
746
// if we have a server, then check through it
748
if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
752
if(findEntryByHash(key) || findEntryByHash(key_out))
758
S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
760
QPtrListIterator<S5BConnection> it(d->incomingConns);
761
for(S5BConnection *c; (c = it.current()); ++it) {
762
if(c->d->peer.compare(from) && c->d->sid == sid)
768
S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
770
QPtrListIterator<Entry> it(d->activeList);
771
for(Entry *e; (e = it.current()); ++it) {
778
S5BManager::Entry *S5BManager::findEntry(Item *i) const
780
QPtrListIterator<Entry> it(d->activeList);
781
for(Entry *e; (e = it.current()); ++it) {
788
S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
790
QPtrListIterator<Entry> it(d->activeList);
791
for(Entry *e; (e = it.current()); ++it) {
792
if(e->i && e->i->key == key)
798
S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
800
QPtrListIterator<Entry> it(d->activeList);
801
for(Entry *e; (e = it.current()); ++it) {
802
if(e->i && e->i->peer.compare(peer) && e->sid == sid)
808
S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
810
const QPtrList<S5BManager> &manList = d->serv->managerList();
811
QPtrListIterator<S5BManager> it(manList);
812
for(S5BManager *m; (m = it.current()); ++it) {
813
Entry *e = m->findEntryByHash(key);
820
bool S5BManager::srv_ownsHash(const QString &key) const
822
if(findEntryByHash(key))
827
void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
829
Entry *e = findEntryByHash(key);
830
if(!e->i->allowIncoming) {
832
SafeDelete::deleteSingle(sc);
835
if(e->c->d->mode == S5BConnection::Datagram)
836
sc->grantUDPAssociate("", 0);
839
e->relatedServer = (S5BServer *)sender();
840
e->i->setIncomingClient(sc);
843
void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data)
845
Entry *e = findEntryByHash(key);
846
if(!e->c->d->mode != S5BConnection::Datagram)
847
return; // this key isn't in udp mode? drop!
851
return; // only init once
853
// lock on to this sender
858
// reply that initialization was successful
859
d->ps->sendUDPSuccess(e->c->d->peer, key);
863
// not initialized yet? something went wrong
867
// must come from same source as when initialized
868
if(addr.toString() != e->udp_addr.toString() || port != e->udp_port)
871
e->c->man_udpReady(data);
874
void S5BManager::srv_unlink()
879
void S5BManager::con_connect(S5BConnection *c)
883
Entry *e = new Entry;
886
d->activeList.append(e);
888
if(c->d->proxy.isValid()) {
895
void S5BManager::con_accept(S5BConnection *c)
897
Entry *e = findEntry(c);
901
if(e->c->d->req.fast) {
902
if(targetShouldOfferProxy(e)) {
910
void S5BManager::con_reject(S5BConnection *c)
912
d->ps->respondError(c->d->peer, c->d->req.id, 406, "Not acceptable");
915
void S5BManager::con_unlink(S5BConnection *c)
917
Entry *e = findEntry(c);
921
// active incoming request? cancel it
922
if(e->i && e->i->conn)
923
d->ps->respondError(e->i->peer, e->i->out_id, 406, "Not acceptable");
925
d->activeList.removeRef(e);
928
void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf)
930
Entry *e = findEntry(c);
937
e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf);
940
void S5BManager::item_accepted()
942
Item *i = (Item *)sender();
943
Entry *e = findEntry(i);
945
e->c->accepted(); // signal
948
void S5BManager::item_tryingHosts(const StreamHostList &list)
950
Item *i = (Item *)sender();
951
Entry *e = findEntry(i);
953
e->c->tryingHosts(list); // signal
956
void S5BManager::item_proxyConnect()
958
Item *i = (Item *)sender();
959
Entry *e = findEntry(i);
961
e->c->proxyConnect(); // signal
964
void S5BManager::item_waitingForActivation()
966
Item *i = (Item *)sender();
967
Entry *e = findEntry(i);
969
e->c->waitingForActivation(); // signal
972
void S5BManager::item_connected()
974
Item *i = (Item *)sender();
975
Entry *e = findEntry(i);
978
SocksClient *client = i->client;
980
SocksUDP *client_udp = i->client_udp;
983
// give it to the connection
984
e->c->man_clientReady(client, client_udp);
987
void S5BManager::item_error(int x)
989
Item *i = (Item *)sender();
990
Entry *e = findEntry(i);
995
void S5BManager::entryContinue(Entry *e)
997
e->i = new Item(this);
998
e->i->proxy = e->proxyInfo;
1000
connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
1001
connect(e->i, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(item_tryingHosts(const StreamHostList &)));
1002
connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
1003
connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
1004
connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
1005
connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
1007
if(e->c->isRemote()) {
1008
const S5BRequest &req = e->c->d->req;
1009
e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.hosts, req.id, req.fast, req.udp);
1012
e->i->startInitiator(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false);
1013
e->c->requesting(); // signal
1017
void S5BManager::queryProxy(Entry *e)
1019
QGuardedPtr<QObject> self = this;
1020
e->c->proxyQuery(); // signal
1025
printf("querying proxy: [%s]\n", e->c->d->proxy.full().latin1());
1027
e->query = new JT_S5B(d->client->rootTask());
1028
connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
1029
e->query->requestProxyInfo(e->c->d->proxy);
1033
void S5BManager::query_finished()
1035
JT_S5B *query = (JT_S5B *)sender();
1038
QPtrListIterator<Entry> it(d->activeList);
1039
for(; (e = it.current()); ++it) {
1040
if(e->query == query) {
1050
printf("query finished: ");
1052
if(query->success()) {
1053
e->proxyInfo = query->proxyInfo();
1055
printf("host/ip=[%s] port=[%d]\n", e->proxyInfo.host().latin1(), e->proxyInfo.port());
1064
QGuardedPtr<QObject> self = this;
1065
e->c->proxyResult(query->success()); // signal
1072
bool S5BManager::targetShouldOfferProxy(Entry *e)
1074
if(!e->c->d->proxy.isValid())
1077
// if target, don't offer any proxy if the initiator already did
1078
const StreamHostList &hosts = e->c->d->req.hosts;
1079
for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1084
// ensure we don't offer the same proxy as the initiator
1085
if(haveHost(hosts, e->c->d->proxy))
1091
//----------------------------------------------------------------------------
1093
//----------------------------------------------------------------------------
1094
S5BManager::Item::Item(S5BManager *manager) : QObject(0)
1108
S5BManager::Item::~Item()
1113
void S5BManager::Item::reset()
1133
delete client_out_udp;
1141
targetMode = Unknown;
1145
connSuccess = false;
1146
localFailed = false;
1147
remoteFailed = false;
1148
allowIncoming = false;
1152
void S5BManager::Item::startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp)
1157
key = makeKey(sid, self, peer);
1158
out_key = makeKey(sid, peer, self);
1163
printf("S5BManager::Item initiating request %s [%s]\n", peer.full().latin1(), sid.latin1());
1169
void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool _fast, bool _udp)
1177
key = makeKey(sid, self, peer);
1178
out_key = makeKey(sid, peer, self);
1182
printf("S5BManager::Item incoming request %s [%s]\n", peer.full().latin1(), sid.latin1());
1190
void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
1194
QGuardedPtr<QObject> self = this;
1199
// if we already have a stream, then bounce this request
1201
m->doError(peer, iq_id, 406, "Not acceptable");
1210
void S5BManager::Item::doOutgoing()
1212
StreamHostList hosts;
1213
S5BServer *serv = m->server();
1214
if(serv && serv->isActive() && !haveHost(in_hosts, m->client()->jid())) {
1215
QStringList hostList = serv->hostList();
1216
for(QStringList::ConstIterator it = hostList.begin(); it != hostList.end(); ++it) {
1218
h.setJid(m->client()->jid());
1220
h.setPort(serv->port());
1225
// if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
1226
if(proxy.jid().isValid())
1229
// if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
1230
if(state == Target && hosts.isEmpty()) {
1235
allowIncoming = true;
1237
task = new JT_S5B(m->client()->rootTask());
1238
connect(task, SIGNAL(finished()), SLOT(jt_finished()));
1239
task->request(peer, sid, hosts, state == Initiator ? wantFast : false, udp);
1240
out_id = task->id();
1244
void S5BManager::Item::doIncoming()
1246
if(in_hosts.isEmpty()) {
1251
StreamHostList list;
1253
// take just the proxy streamhosts
1254
for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
1261
// only try doing the late proxy trick if using fast mode AND we did not offer a proxy
1262
if((state == Initiator || (state == Target && fast)) && !proxy.jid().isValid()) {
1263
// take just the non-proxy streamhosts
1264
bool hasProxies = false;
1265
for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
1274
// no regular streamhosts? wait for remote error
1283
conn = new S5BConnector;
1284
connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
1286
QGuardedPtr<QObject> self = this;
1291
conn->start(m->client()->jid(), list, out_key, udp, lateProxy ? 10 : 30);
1294
void S5BManager::Item::setIncomingClient(SocksClient *sc)
1297
printf("S5BManager::Item: %s [%s] successful incoming connection\n", peer.full().latin1(), sid.latin1());
1300
connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1301
connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1302
connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1305
allowIncoming = false;
1308
void S5BManager::Item::incomingActivate(const Jid &streamHost)
1311
activatedStream = streamHost;
1312
checkForActivation();
1316
void S5BManager::Item::jt_finished()
1322
printf("jt_finished: state=%s, %s\n", state == Initiator ? "initiator" : "target", j->success() ? "ok" : "fail");
1325
if(state == Initiator) {
1326
if(targetMode == Unknown) {
1327
targetMode = NotFast;
1328
QGuardedPtr<QObject> self = this;
1335
// if we've already reported successfully connecting to them, then this response doesn't matter
1336
if(state == Initiator && connSuccess) {
1342
// stop connecting out
1343
if(conn || lateProxy) {
1349
Jid streamHost = j->streamHostUsed();
1351
// they connected to us?
1352
if(streamHost.compare(self)) {
1354
if(state == Initiator) {
1355
activatedStream = streamHost;
1359
checkForActivation();
1363
printf("S5BManager::Item %s claims to have connected to us, but we don't see this\n", peer.full().latin1());
1366
error(ErrWrongHost);
1369
else if(streamHost.compare(proxy.jid())) {
1370
// toss out any direct incoming, since it won't be used
1373
allowIncoming = false;
1376
printf("attempting to connect to proxy\n");
1378
// connect to the proxy
1379
proxy_conn = new S5BConnector;
1380
connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
1381
StreamHostList list;
1384
QGuardedPtr<QObject> self = this;
1389
proxy_conn->start(m->client()->jid(), list, key, udp, 30);
1393
printf("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", peer.full().latin1());
1396
error(ErrWrongHost);
1401
printf("S5BManager::Item %s [%s] error\n", peer.full().latin1(), sid.latin1());
1403
remoteFailed = true;
1404
statusCode = j->statusCode();
1411
// if connSuccess is true at this point, then we're a Target
1413
checkForActivation();
1420
void S5BManager::Item::conn_result(bool b)
1423
SocksClient *sc = conn->takeClient();
1424
SocksUDP *sc_udp = conn->takeUDP();
1425
StreamHost h = conn->streamHostUsed();
1431
printf("S5BManager::Item: %s [%s] successful outgoing connection\n", peer.full().latin1(), sid.latin1());
1434
connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1435
connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1436
connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1438
m->doSuccess(peer, in_id, h.jid());
1440
// if the first batch works, don't try proxy
1443
// if initiator, run with this one
1444
if(state == Initiator) {
1445
// if we had an incoming one, toss it
1447
client_udp = sc_udp;
1450
allowIncoming = false;
1451
activatedStream = peer;
1455
client_out_udp = sc_udp;
1457
checkForActivation();
1464
// if we delayed the proxies for later, try now
1474
void S5BManager::Item::proxy_result(bool b)
1477
printf("proxy_result: %s\n", b ? "ok" : "fail");
1480
SocksClient *sc = proxy_conn->takeClient();
1481
SocksUDP *sc_udp = proxy_conn->takeUDP();
1485
connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
1486
connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
1487
connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
1490
client_udp = sc_udp;
1494
printf("activating proxy stream\n");
1496
proxy_task = new JT_S5B(m->client()->rootTask());
1497
connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
1498
proxy_task->requestActivation(proxy.jid(), sid, peer);
1499
proxy_task->go(true);
1509
void S5BManager::Item::proxy_finished()
1511
JT_S5B *j = proxy_task;
1516
printf("proxy stream activated\n");
1518
if(state == Initiator) {
1519
activatedStream = proxy.jid();
1523
checkForActivation();
1531
void S5BManager::Item::sc_readyRead()
1534
printf("sc_readyRead\n");
1536
// only targets check for activation, and only should do it if there is no pending outgoing iq-set
1537
if(state == Target && !task && !proxy_task)
1538
checkForActivation();
1541
void S5BManager::Item::sc_bytesWritten(int)
1544
printf("sc_bytesWritten\n");
1546
// this should only happen to the initiator, and should always be 1 byte (the '\r' sent earlier)
1550
void S5BManager::Item::sc_error(int)
1553
printf("sc_error\n");
1559
void S5BManager::Item::doConnectError()
1562
m->doError(peer, in_id, 404, "Could not connect to given hosts");
1566
void S5BManager::Item::tryActivation()
1569
printf("tryActivation\n");
1573
printf("already activated !?\n");
1578
if(targetMode == NotFast) {
1580
printf("tryActivation: NotFast\n");
1582
// nothing to activate, we're done
1585
else if(targetMode == Fast) {
1586
// with fast mode, we don't wait for the iq reply, so delete the task (if any)
1592
// if udp, activate using special stanza
1594
m->doActivate(peer, sid, activatedStream);
1598
printf("sending extra CR\n");
1600
// must send [CR] to activate target streamhost
1608
void S5BManager::Item::checkForActivation()
1610
QPtrList<SocksClient> clientList;
1612
clientList.append(client);
1614
clientList.append(client_out);
1615
QPtrListIterator<SocksClient> it(clientList);
1616
for(SocksClient *sc; (sc = it.current()); ++it) {
1618
printf("checking for activation\n");
1623
if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) {
1624
clientList.removeRef(sc);
1630
printf("need CR\n");
1632
if(sc->bytesAvailable() >= 1) {
1633
clientList.removeRef(sc);
1634
QByteArray a = sc->read(1);
1644
SocksUDP *sc_udp = 0;
1646
delete client_out_udp;
1648
sc_udp = client_udp;
1650
else if(sc == client_out) {
1653
sc_udp = client_out_udp;
1656
sc->disconnect(this);
1657
clientList.setAutoDelete(true);
1661
client_udp = sc_udp;
1664
printf("activation success\n");
1671
printf("not fast mode, no need to wait for anything\n");
1673
clientList.removeRef(sc);
1674
sc->disconnect(this);
1675
clientList.setAutoDelete(true);
1688
// only emit waitingForActivation if there is nothing left to do
1689
if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
1690
waitingForActivation();
1694
void S5BManager::Item::checkFailure()
1696
bool failed = false;
1697
if(state == Initiator) {
1699
if((localFailed && targetMode == Fast) || targetMode == NotFast)
1705
if((remoteFailed && fast) || !fast)
1711
if(state == Initiator) {
1713
if(statusCode == 404)
1725
void S5BManager::Item::finished()
1727
client->disconnect(this);
1730
printf("S5BManager::Item %s [%s] linked successfully\n", peer.full().latin1(), sid.latin1());
1735
//----------------------------------------------------------------------------
1737
//----------------------------------------------------------------------------
1738
class S5BConnector::Item : public QObject
1742
SocksClient *client;
1743
SocksUDP *client_udp;
1751
Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : QObject(0)
1757
client = new SocksClient;
1759
connect(client, SIGNAL(connected()), SLOT(sc_connected()));
1760
connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1761
connect(&t, SIGNAL(timeout()), SLOT(trySendUDP()));
1771
client->connectToHost(host.host(), host.port(), key, 0, udp);
1777
client_udp->change(key, 0); // flip over to the data port
1787
// if udp, need to send init packet before we are good
1790
client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort());
1803
printf("S5BConnector[%s]: error\n", host.host().latin1());
1811
if(udp_tries == 5) {
1818
// send initialization with our JID
1819
QCString cs = jid.full().utf8();
1820
QByteArray a(cs.length());
1821
memcpy(a.data(), cs.data(), a.size());
1822
client_udp->write(a);
1838
printf("S5BConnector[%s]: success\n", host.host().latin1());
1840
client->disconnect(this);
1845
class S5BConnector::Private
1848
SocksClient *active;
1849
SocksUDP *active_udp;
1850
QPtrList<Item> itemList;
1852
StreamHost activeHost;
1856
S5BConnector::S5BConnector(QObject *parent)
1862
d->itemList.setAutoDelete(true);
1863
connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
1866
S5BConnector::~S5BConnector()
1872
void S5BConnector::reset()
1875
delete d->active_udp;
1879
d->itemList.clear();
1882
void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout)
1887
printf("S5BConnector: starting [%p]!\n", this);
1889
for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
1890
Item *i = new Item(self, *it, key, udp);
1891
connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
1892
d->itemList.append(i);
1895
d->t.start(timeout * 1000);
1898
SocksClient *S5BConnector::takeClient()
1900
SocksClient *c = d->active;
1905
SocksUDP *S5BConnector::takeUDP()
1907
SocksUDP *c = d->active_udp;
1912
StreamHost S5BConnector::streamHostUsed() const
1914
return d->activeHost;
1917
void S5BConnector::item_result(bool b)
1919
Item *i = (Item *)sender();
1921
d->active = i->client;
1923
d->active_udp = i->client_udp;
1925
d->activeHost = i->host;
1926
d->itemList.clear();
1929
printf("S5BConnector: complete! [%p]\n", this);
1934
d->itemList.removeRef(i);
1935
if(d->itemList.isEmpty()) {
1938
printf("S5BConnector: failed! [%p]\n", this);
1945
void S5BConnector::t_timeout()
1949
printf("S5BConnector: failed! (timeout)\n");
1954
void S5BConnector::man_udpSuccess(const Jid &streamHost)
1956
// was anyone sending to this streamhost?
1957
QPtrListIterator<Item> it(d->itemList);
1958
for(Item *i; (i = it.current()); ++it) {
1959
if(i->host.jid().compare(streamHost) && i->client_udp) {
1966
//----------------------------------------------------------------------------
1968
//----------------------------------------------------------------------------
1969
class S5BServer::Item : public QObject
1973
SocksClient *client;
1977
Item(SocksClient *c) : QObject(0)
1980
connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
1981
connect(client, SIGNAL(incomingConnectRequest(const QString &, int)), SLOT(sc_incomingConnectRequest(const QString &, int)));
1982
connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
1984
connect(&expire, SIGNAL(timeout()), SLOT(doError()));
1993
void resetExpiration()
1995
expire.start(30000);
2010
void sc_incomingMethods(int m)
2012
if(m & SocksClient::AuthNone)
2013
client->chooseMethod(SocksClient::AuthNone);
2018
void sc_incomingConnectRequest(const QString &_host, int port)
2022
client->disconnect(this);
2035
class S5BServer::Private
2039
QStringList hostList;
2040
QPtrList<S5BManager> manList;
2041
QPtrList<Item> itemList;
2044
S5BServer::S5BServer(QObject *parent)
2048
d->itemList.setAutoDelete(true);
2049
connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
2050
connect(&d->serv, SIGNAL(incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)), SLOT(ss_incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)));
2053
S5BServer::~S5BServer()
2059
bool S5BServer::isActive() const
2061
return d->serv.isActive();
2064
bool S5BServer::start(int port)
2067
return d->serv.listen(port, true);
2070
void S5BServer::stop()
2075
void S5BServer::setHostList(const QStringList &list)
2080
QStringList S5BServer::hostList() const
2085
int S5BServer::port() const
2087
return d->serv.port();
2090
void S5BServer::ss_incomingReady()
2092
Item *i = new Item(d->serv.takeIncoming());
2094
printf("S5BServer: incoming connection from %s:%d\n", i->client->peerAddress().toString().latin1(), i->client->peerPort());
2096
connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
2097
d->itemList.append(i);
2100
void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data)
2102
if(port != 0 || port != 1)
2105
QPtrListIterator<S5BManager> it(d->manList);
2106
for(S5BManager *m; (m = it.current()); ++it) {
2107
if(m->srv_ownsHash(host)) {
2108
m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data);
2114
void S5BServer::item_result(bool b)
2116
Item *i = (Item *)sender();
2118
printf("S5BServer item result: %d\n", b);
2121
d->itemList.removeRef(i);
2125
SocksClient *c = i->client;
2127
QString key = i->host;
2128
d->itemList.removeRef(i);
2130
// find the appropriate manager for this incoming connection
2131
QPtrListIterator<S5BManager> it(d->manList);
2132
for(S5BManager *m; (m = it.current()); ++it) {
2133
if(m->srv_ownsHash(key)) {
2134
m->srv_incomingReady(c, key);
2143
void S5BServer::link(S5BManager *m)
2145
d->manList.append(m);
2148
void S5BServer::unlink(S5BManager *m)
2150
d->manList.removeRef(m);
2153
void S5BServer::unlinkAll()
2155
QPtrListIterator<S5BManager> it(d->manList);
2156
for(S5BManager *m; (m = it.current()); ++it)
2161
const QPtrList<S5BManager> & S5BServer::managerList() const
2166
void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
2168
d->serv.writeUDP(addr, port, data);
2171
//----------------------------------------------------------------------------
2173
//----------------------------------------------------------------------------
2174
class JT_S5B::Private
2180
StreamHost proxyInfo;
2185
JT_S5B::JT_S5B(Task *parent)
2190
connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
2198
void JT_S5B::request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp)
2204
iq = createIQ(doc(), "set", to.full(), id());
2205
QDomElement query = doc()->createElement("query");
2206
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
2207
query.setAttribute("sid", sid);
2208
query.setAttribute("mode", udp ? "udp" : "tcp" );
2209
iq.appendChild(query);
2210
for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
2211
QDomElement shost = doc()->createElement("streamhost");
2212
shost.setAttribute("jid", (*it).jid().full());
2213
shost.setAttribute("host", (*it).host());
2214
shost.setAttribute("port", QString::number((*it).port()));
2215
if((*it).isProxy()) {
2216
QDomElement p = doc()->createElement("proxy");
2217
p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2218
shost.appendChild(p);
2220
query.appendChild(shost);
2223
QDomElement e = doc()->createElement("fast");
2224
e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2225
query.appendChild(e);
2230
void JT_S5B::requestProxyInfo(const Jid &to)
2236
iq = createIQ(doc(), "get", to.full(), id());
2237
QDomElement query = doc()->createElement("query");
2238
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
2239
iq.appendChild(query);
2243
void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
2249
iq = createIQ(doc(), "set", to.full(), id());
2250
QDomElement query = doc()->createElement("query");
2251
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
2252
query.setAttribute("sid", sid);
2253
iq.appendChild(query);
2254
QDomElement act = doc()->createElement("activate");
2255
act.appendChild(doc()->createTextNode(target.full()));
2256
query.appendChild(act);
2263
d->t.start(15000, true);
2267
void JT_S5B::onDisconnect()
2272
bool JT_S5B::take(const QDomElement &x)
2277
if(!iqVerify(x, d->to, id()))
2282
if(x.attribute("type") == "result") {
2283
QDomElement q = queryTag(x);
2287
QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
2289
d->streamHost = shost.attribute("jid");
2294
else if(d->mode == 1) {
2296
QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
2297
if(!shost.isNull()) {
2298
Jid j = shost.attribute("jid");
2300
QString host = shost.attribute("host");
2301
if(!host.isEmpty()) {
2302
int port = shost.attribute("port").toInt();
2327
void JT_S5B::t_timeout()
2330
setError(500, "Timed out");
2333
Jid JT_S5B::streamHostUsed() const
2335
return d->streamHost;
2338
StreamHost JT_S5B::proxyInfo() const
2340
return d->proxyInfo;
2343
//----------------------------------------------------------------------------
2345
//----------------------------------------------------------------------------
2346
JT_PushS5B::JT_PushS5B(Task *parent)
2351
JT_PushS5B::~JT_PushS5B()
2355
int JT_PushS5B::priority() const
2360
bool JT_PushS5B::take(const QDomElement &e)
2362
// look for udpsuccess
2363
if(e.tagName() == "message") {
2364
QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement();
2365
if(!x.isNull() && x.attribute("xmlns") == "http://jabber.org/protocol/bytestreams") {
2366
incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr"));
2369
x = e.elementsByTagName("activate").item(0).toElement();
2370
if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") {
2371
incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid")));
2377
// must be an iq-set tag
2378
if(e.tagName() != "iq")
2380
if(e.attribute("type") != "set")
2382
if(queryNS(e) != "http://jabber.org/protocol/bytestreams")
2385
Jid from(e.attribute("from"));
2386
QDomElement q = queryTag(e);
2387
QString sid = q.attribute("sid");
2389
StreamHostList hosts;
2390
QDomNodeList nl = q.elementsByTagName("streamhost");
2391
for(uint n = 0; n < nl.count(); ++n) {
2392
QDomElement shost = nl.item(n).toElement();
2393
if(hosts.count() < MAXSTREAMHOSTS) {
2394
Jid j = shost.attribute("jid");
2397
QString host = shost.attribute("host");
2400
int port = shost.attribute("port").toInt();
2401
QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
2402
bool isProxy = false;
2403
if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
2410
h.setIsProxy(isProxy);
2417
t = q.elementsByTagName("fast").item(0).toElement();
2418
if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
2423
r.id = e.attribute("id");
2427
r.udp = q.attribute("mode") == "udp" ? true: false;
2433
void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
2435
QDomElement iq = createIQ(doc(), "result", to.full(), id);
2436
QDomElement query = doc()->createElement("query");
2437
query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
2438
iq.appendChild(query);
2439
QDomElement shost = doc()->createElement("streamhost-used");
2440
shost.setAttribute("jid", streamHost.full());
2441
query.appendChild(shost);
2445
void JT_PushS5B::respondError(const Jid &to, const QString &id, int code, const QString &str)
2447
QDomElement iq = createIQ(doc(), "error", to.full(), id);
2448
QDomElement err = textTag(doc(), "error", str);
2449
err.setAttribute("code", QString::number(code));
2450
iq.appendChild(err);
2454
void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr)
2456
QDomElement m = doc()->createElement("message");
2457
m.setAttribute("to", to.full());
2458
QDomElement u = doc()->createElement("udpsuccess");
2459
u.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
2460
u.setAttribute("dstaddr", dstaddr);
2465
void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &streamHost)
2467
QDomElement m = doc()->createElement("message");
2468
m.setAttribute("to", to.full());
2469
QDomElement act = doc()->createElement("activate");
2470
act.setAttribute("xmlns", "http://affinix.com/jabber/stream");
2471
act.setAttribute("sid", sid);
2472
act.setAttribute("jid", streamHost.full());
2477
//----------------------------------------------------------------------------
2479
//----------------------------------------------------------------------------
2480
StreamHost::StreamHost()
2486
const Jid & StreamHost::jid() const
2491
const QString & StreamHost::host() const
2496
int StreamHost::port() const
2501
bool StreamHost::isProxy() const
2506
void StreamHost::setJid(const Jid &_j)
2511
void StreamHost::setHost(const QString &host)
2516
void StreamHost::setPort(int port)
2521
void StreamHost::setIsProxy(bool b)