From bcb704366cb5e333a626c18c308c7e0448a8e69f Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- .../jabber/libiris/cutestuff/network/socks.cpp | 1223 ++++++++++++++++++++ 1 file changed, 1223 insertions(+) create mode 100644 kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp (limited to 'kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp') diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp new file mode 100644 index 00000000..bae374f5 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp @@ -0,0 +1,1223 @@ +/* + * socks.cpp - SOCKS5 TCP proxy client/server + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"socks.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_UNIX +#include +#include +#endif + +#ifdef Q_OS_WIN32 +#include +#endif + +#include"servsock.h" +#include"bsocket.h" + +#ifdef PROX_DEBUG +#include +#endif + +// CS_NAMESPACE_BEGIN + +//---------------------------------------------------------------------------- +// SocksUDP +//---------------------------------------------------------------------------- +static QByteArray sp_create_udp(const QString &host, Q_UINT16 port, const QByteArray &buf) +{ + // detect for IP addresses + //QHostAddress addr; + //if(addr.setAddress(host)) + // return sp_set_request(addr, port, cmd1); + + QCString h = host.utf8(); + h.truncate(255); + h = QString::fromUtf8(h).utf8(); // delete any partial characters? + int hlen = h.length(); + + int at = 0; + QByteArray a(4); + a[at++] = 0x00; // reserved + a[at++] = 0x00; // reserved + a[at++] = 0x00; // frag + a[at++] = 0x03; // address type = domain + + // host + a.resize(at+hlen+1); + a[at++] = hlen; + memcpy(a.data() + at, h.data(), hlen); + at += hlen; + + // port + a.resize(at+2); + unsigned short p = htons(port); + memcpy(a.data() + at, &p, 2); + at += 2; + + a.resize(at+buf.size()); + memcpy(a.data() + at, buf.data(), buf.size()); + + return a; +} + +struct SPS_UDP +{ + QString host; + Q_UINT16 port; + QByteArray data; +}; + +static int sp_read_udp(QByteArray *from, SPS_UDP *s) +{ + int full_len = 4; + if((int)from->size() < full_len) + return 0; + + QString host; + QHostAddress addr; + unsigned char atype = from->at(3); + + if(atype == 0x01) { + full_len += 4; + if((int)from->size() < full_len) + return 0; + Q_UINT32 ip4; + memcpy(&ip4, from->data() + 4, 4); + addr.setAddress(ntohl(ip4)); + host = addr.toString(); + } + else if(atype == 0x03) { + ++full_len; + if((int)from->size() < full_len) + return 0; + unsigned char host_len = from->at(4); + full_len += host_len; + if((int)from->size() < full_len) + return 0; + QCString cs(host_len+1); + memcpy(cs.data(), from->data() + 5, host_len); + host = QString::fromLatin1(cs); + } + else if(atype == 0x04) { + full_len += 16; + if((int)from->size() < full_len) + return 0; + Q_UINT8 a6[16]; + memcpy(a6, from->data() + 4, 16); + addr.setAddress(a6); + host = addr.toString(); + } + + full_len += 2; + if((int)from->size() < full_len) + return 0; + + Q_UINT16 p; + memcpy(&p, from->data() + full_len - 2, 2); + + s->host = host; + s->port = ntohs(p); + s->data.resize(from->size() - full_len); + memcpy(s->data.data(), from->data() + full_len, s->data.size()); + + return 1; +} + +class SocksUDP::Private +{ +public: + QSocketDevice *sd; + QSocketNotifier *sn; + SocksClient *sc; + QHostAddress routeAddr; + int routePort; + QString host; + int port; +}; + +SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort) +:QObject(sc) +{ + d = new Private; + d->sc = sc; + d->sd = new QSocketDevice(QSocketDevice::Datagram); + d->sd->setBlocking(false); + d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read); + connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int))); + d->host = host; + d->port = port; + d->routeAddr = routeAddr; + d->routePort = routePort; +} + +SocksUDP::~SocksUDP() +{ + delete d->sn; + delete d->sd; + delete d; +} + +void SocksUDP::change(const QString &host, int port) +{ + d->host = host; + d->port = port; +} + +void SocksUDP::write(const QByteArray &data) +{ + QByteArray buf = sp_create_udp(d->host, d->port, data); + d->sd->setBlocking(true); + d->sd->writeBlock(buf.data(), buf.size(), d->routeAddr, d->routePort); + d->sd->setBlocking(false); +} + +void SocksUDP::sn_activated(int) +{ + QByteArray buf(8192); + int actual = d->sd->readBlock(buf.data(), buf.size()); + buf.resize(actual); + packetReady(buf); +} + +//---------------------------------------------------------------------------- +// SocksClient +//---------------------------------------------------------------------------- +#define REQ_CONNECT 0x01 +#define REQ_BIND 0x02 +#define REQ_UDPASSOCIATE 0x03 + +#define RET_SUCCESS 0x00 +#define RET_UNREACHABLE 0x04 +#define RET_CONNREFUSED 0x05 + +// spc = socks packet client +// sps = socks packet server +// SPCS = socks packet client struct +// SPSS = socks packet server struct + +// Version +static QByteArray spc_set_version() +{ + QByteArray ver(4); + ver[0] = 0x05; // socks version 5 + ver[1] = 0x02; // number of methods + ver[2] = 0x00; // no-auth + ver[3] = 0x02; // username + return ver; +} + +static QByteArray sps_set_version(int method) +{ + QByteArray ver(2); + ver[0] = 0x05; + ver[1] = method; + return ver; +} + +struct SPCS_VERSION +{ + unsigned char version; + QByteArray methodList; +}; + +static int spc_get_version(QByteArray *from, SPCS_VERSION *s) +{ + if(from->size() < 1) + return 0; + if(from->at(0) != 0x05) // only SOCKS5 supported + return -1; + if(from->size() < 2) + return 0; + uint num = from->at(1); + if(num > 16) // who the heck has over 16 auth methods?? + return -1; + if(from->size() < 2 + num) + return 0; + QByteArray a = ByteStream::takeArray(from, 2+num); + s->version = a[0]; + s->methodList.resize(num); + memcpy(s->methodList.data(), a.data() + 2, num); + return 1; +} + +struct SPSS_VERSION +{ + unsigned char version; + unsigned char method; +}; + +static int sps_get_version(QByteArray *from, SPSS_VERSION *s) +{ + if(from->size() < 2) + return 0; + QByteArray a = ByteStream::takeArray(from, 2); + s->version = a[0]; + s->method = a[1]; + return 1; +} + +// authUsername +static QByteArray spc_set_authUsername(const QCString &user, const QCString &pass) +{ + int len1 = user.length(); + int len2 = pass.length(); + if(len1 > 255) + len1 = 255; + if(len2 > 255) + len2 = 255; + QByteArray a(1+1+len1+1+len2); + a[0] = 0x01; // username auth version 1 + a[1] = len1; + memcpy(a.data() + 2, user.data(), len1); + a[2+len1] = len2; + memcpy(a.data() + 3 + len1, pass.data(), len2); + return a; +} + +static QByteArray sps_set_authUsername(bool success) +{ + QByteArray a(2); + a[0] = 0x01; + a[1] = success ? 0x00 : 0xff; + return a; +} + +struct SPCS_AUTHUSERNAME +{ + QString user, pass; +}; + +static int spc_get_authUsername(QByteArray *from, SPCS_AUTHUSERNAME *s) +{ + if(from->size() < 1) + return 0; + unsigned char ver = from->at(0); + if(ver != 0x01) + return -1; + if(from->size() < 2) + return 0; + unsigned char ulen = from->at(1); + if((int)from->size() < ulen + 3) + return 0; + unsigned char plen = from->at(ulen+2); + if((int)from->size() < ulen + plen + 3) + return 0; + QByteArray a = ByteStream::takeArray(from, ulen + plen + 3); + + QCString user, pass; + user.resize(ulen+1); + pass.resize(plen+1); + memcpy(user.data(), a.data()+2, ulen); + memcpy(pass.data(), a.data()+ulen+3, plen); + s->user = QString::fromUtf8(user); + s->pass = QString::fromUtf8(pass); + return 1; +} + +struct SPSS_AUTHUSERNAME +{ + unsigned char version; + bool success; +}; + +static int sps_get_authUsername(QByteArray *from, SPSS_AUTHUSERNAME *s) +{ + if(from->size() < 2) + return 0; + QByteArray a = ByteStream::takeArray(from, 2); + s->version = a[0]; + s->success = a[1] == 0 ? true: false; + return 1; +} + +// connectRequest +static QByteArray sp_set_request(const QHostAddress &addr, unsigned short port, unsigned char cmd1) +{ + int at = 0; + QByteArray a(4); + a[at++] = 0x05; // socks version 5 + a[at++] = cmd1; + a[at++] = 0x00; // reserved + if(addr.isIp4Addr()) { + a[at++] = 0x01; // address type = ipv4 + Q_UINT32 ip4 = htonl(addr.ip4Addr()); + a.resize(at+4); + memcpy(a.data() + at, &ip4, 4); + at += 4; + } + else { + a[at++] = 0x04; + Q_UINT8 a6[16]; + QStringList s6 = QStringList::split(':', addr.toString(), true); + int at = 0; + Q_UINT16 c; + bool ok; + for(QStringList::ConstIterator it = s6.begin(); it != s6.end(); ++it) { + c = (*it).toInt(&ok, 16); + a6[at++] = (c >> 8); + a6[at++] = c & 0xff; + } + a.resize(at+16); + memcpy(a.data() + at, a6, 16); + at += 16; + } + + // port + a.resize(at+2); + unsigned short p = htons(port); + memcpy(a.data() + at, &p, 2); + + return a; +} + +static QByteArray sp_set_request(const QString &host, Q_UINT16 port, unsigned char cmd1) +{ + // detect for IP addresses + QHostAddress addr; + if(addr.setAddress(host)) + return sp_set_request(addr, port, cmd1); + + QCString h = host.utf8(); + h.truncate(255); + h = QString::fromUtf8(h).utf8(); // delete any partial characters? + int hlen = h.length(); + + int at = 0; + QByteArray a(4); + a[at++] = 0x05; // socks version 5 + a[at++] = cmd1; + a[at++] = 0x00; // reserved + a[at++] = 0x03; // address type = domain + + // host + a.resize(at+hlen+1); + a[at++] = hlen; + memcpy(a.data() + at, h.data(), hlen); + at += hlen; + + // port + a.resize(at+2); + unsigned short p = htons(port); + memcpy(a.data() + at, &p, 2); + + return a; +} + +struct SPS_CONNREQ +{ + unsigned char version; + unsigned char cmd; + int address_type; + QString host; + QHostAddress addr; + Q_UINT16 port; +}; + +static int sp_get_request(QByteArray *from, SPS_CONNREQ *s) +{ + int full_len = 4; + if((int)from->size() < full_len) + return 0; + + QString host; + QHostAddress addr; + unsigned char atype = from->at(3); + + if(atype == 0x01) { + full_len += 4; + if((int)from->size() < full_len) + return 0; + Q_UINT32 ip4; + memcpy(&ip4, from->data() + 4, 4); + addr.setAddress(ntohl(ip4)); + } + else if(atype == 0x03) { + ++full_len; + if((int)from->size() < full_len) + return 0; + unsigned char host_len = from->at(4); + full_len += host_len; + if((int)from->size() < full_len) + return 0; + QCString cs(host_len+1); + memcpy(cs.data(), from->data() + 5, host_len); + host = QString::fromLatin1(cs); + } + else if(atype == 0x04) { + full_len += 16; + if((int)from->size() < full_len) + return 0; + Q_UINT8 a6[16]; + memcpy(a6, from->data() + 4, 16); + addr.setAddress(a6); + } + + full_len += 2; + if((int)from->size() < full_len) + return 0; + + QByteArray a = ByteStream::takeArray(from, full_len); + + Q_UINT16 p; + memcpy(&p, a.data() + full_len - 2, 2); + + s->version = a[0]; + s->cmd = a[1]; + s->address_type = atype; + s->host = host; + s->addr = addr; + s->port = ntohs(p); + + return 1; +} + +enum { StepVersion, StepAuth, StepRequest }; + +class SocksClient::Private +{ +public: + Private() {} + + BSocket sock; + QString host; + int port; + QString user, pass; + QString real_host; + int real_port; + + QByteArray recvBuf; + bool active; + int step; + int authMethod; + bool incoming, waiting; + + QString rhost; + int rport; + + int pending; + + bool udp; + QString udpAddr; + int udpPort; +}; + +SocksClient::SocksClient(QObject *parent) +:ByteStream(parent) +{ + init(); + + d->incoming = false; +} + +SocksClient::SocksClient(int s, QObject *parent) +:ByteStream(parent) +{ + init(); + + d->incoming = true; + d->waiting = true; + d->sock.setSocket(s); +} + +void SocksClient::init() +{ + d = new Private; + connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected())); + connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); + connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished())); + connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); + connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int))); + connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int))); + + reset(true); +} + +SocksClient::~SocksClient() +{ + reset(true); + delete d; +} + +void SocksClient::reset(bool clear) +{ + if(d->sock.state() != BSocket::Idle) + d->sock.close(); + if(clear) + clearReadBuffer(); + d->recvBuf.resize(0); + d->active = false; + d->waiting = false; + d->udp = false; + d->pending = 0; +} + +bool SocksClient::isIncoming() const +{ + return d->incoming; +} + +void SocksClient::setAuth(const QString &user, const QString &pass) +{ + d->user = user; + d->pass = pass; +} + +void SocksClient::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode) +{ + reset(true); + + d->host = proxyHost; + d->port = proxyPort; + d->real_host = host; + d->real_port = port; + d->udp = udpMode; + +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Connecting to %s:%d", proxyHost.latin1(), proxyPort); + if(d->user.isEmpty()) + fprintf(stderr, "\n"); + else + fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); +#endif + d->sock.connectToHost(d->host, d->port); +} + +bool SocksClient::isOpen() const +{ + return d->active; +} + +void SocksClient::close() +{ + d->sock.close(); + if(d->sock.bytesToWrite() == 0) + reset(); +} + +void SocksClient::writeData(const QByteArray &buf) +{ + d->pending += buf.size(); + d->sock.write(buf); +} + +void SocksClient::write(const QByteArray &buf) +{ + if(d->active && !d->udp) + d->sock.write(buf); +} + +QByteArray SocksClient::read(int bytes) +{ + return ByteStream::read(bytes); +} + +int SocksClient::bytesAvailable() const +{ + return ByteStream::bytesAvailable(); +} + +int SocksClient::bytesToWrite() const +{ + if(d->active) + return d->sock.bytesToWrite(); + else + return 0; +} + +void SocksClient::sock_connected() +{ +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Connected\n"); +#endif + + d->step = StepVersion; + writeData(spc_set_version()); +} + +void SocksClient::sock_connectionClosed() +{ + if(d->active) { + reset(); + connectionClosed(); + } + else { + error(ErrProxyNeg); + } +} + +void SocksClient::sock_delayedCloseFinished() +{ + if(d->active) { + reset(); + delayedCloseFinished(); + } +} + +void SocksClient::sock_readyRead() +{ + QByteArray block = d->sock.read(); + + if(!d->active) { + if(d->incoming) + processIncoming(block); + else + processOutgoing(block); + } + else { + if(!d->udp) { + appendRead(block); + readyRead(); + } + } +} + +void SocksClient::processOutgoing(const QByteArray &block) +{ +#ifdef PROX_DEBUG + // show hex + fprintf(stderr, "SocksClient: client recv { "); + for(int n = 0; n < (int)block.size(); ++n) + fprintf(stderr, "%02X ", (unsigned char)block[n]); + fprintf(stderr, " } \n"); +#endif + ByteStream::appendArray(&d->recvBuf, block); + + if(d->step == StepVersion) { + SPSS_VERSION s; + int r = sps_get_version(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.version != 0x05 || s.method == 0xff) { +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Method selection failed\n"); +#endif + reset(true); + error(ErrProxyNeg); + return; + } + + QString str; + if(s.method == 0x00) { + str = "None"; + d->authMethod = AuthNone; + } + else if(s.method == 0x02) { + str = "Username/Password"; + d->authMethod = AuthUsername; + } + else { +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Server wants to use unknown method '%02x'\n", s.method); +#endif + reset(true); + error(ErrProxyNeg); + return; + } + + if(d->authMethod == AuthNone) { + // no auth, go straight to the request + do_request(); + } + else if(d->authMethod == AuthUsername) { + d->step = StepAuth; +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Authenticating [Username] ...\n"); +#endif + writeData(spc_set_authUsername(d->user.latin1(), d->pass.latin1())); + } + } + } + if(d->step == StepAuth) { + if(d->authMethod == AuthUsername) { + SPSS_AUTHUSERNAME s; + int r = sps_get_authUsername(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.version != 0x01) { + reset(true); + error(ErrProxyNeg); + return; + } + if(!s.success) { + reset(true); + error(ErrProxyAuth); + return; + } + + do_request(); + } + } + } + else if(d->step == StepRequest) { + SPS_CONNREQ s; + int r = sp_get_request(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.cmd != RET_SUCCESS) { +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: client << Error >> [%02x]\n", s.cmd); +#endif + reset(true); + if(s.cmd == RET_UNREACHABLE) + error(ErrHostNotFound); + else if(s.cmd == RET_CONNREFUSED) + error(ErrConnectionRefused); + else + error(ErrProxyNeg); + return; + } + +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: client << Success >>\n"); +#endif + if(d->udp) { + if(s.address_type == 0x03) + d->udpAddr = s.host; + else + d->udpAddr = s.addr.toString(); + d->udpPort = s.port; + } + + d->active = true; + + QGuardedPtr self = this; + connected(); + if(!self) + return; + + if(!d->recvBuf.isEmpty()) { + appendRead(d->recvBuf); + d->recvBuf.resize(0); + readyRead(); + } + } + } +} + +void SocksClient::do_request() +{ +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: Requesting ...\n"); +#endif + d->step = StepRequest; + int cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT; + QByteArray buf; + if(!d->real_host.isEmpty()) + buf = sp_set_request(d->real_host, d->real_port, cmd); + else + buf = sp_set_request(QHostAddress(), 0, cmd); + writeData(buf); +} + +void SocksClient::sock_bytesWritten(int x) +{ + int bytes = x; + if(d->pending >= bytes) { + d->pending -= bytes; + bytes = 0; + } + else { + bytes -= d->pending; + d->pending = 0; + } + if(bytes > 0) + bytesWritten(bytes); +} + +void SocksClient::sock_error(int x) +{ + if(d->active) { + reset(); + error(ErrRead); + } + else { + reset(true); + if(x == BSocket::ErrHostNotFound) + error(ErrProxyConnect); + else if(x == BSocket::ErrConnectionRefused) + error(ErrProxyConnect); + else if(x == BSocket::ErrRead) + error(ErrProxyNeg); + } +} + +void SocksClient::serve() +{ + d->waiting = false; + d->step = StepVersion; + continueIncoming(); +} + +void SocksClient::processIncoming(const QByteArray &block) +{ +#ifdef PROX_DEBUG + // show hex + fprintf(stderr, "SocksClient: server recv { "); + for(int n = 0; n < (int)block.size(); ++n) + fprintf(stderr, "%02X ", (unsigned char)block[n]); + fprintf(stderr, " } \n"); +#endif + ByteStream::appendArray(&d->recvBuf, block); + + if(!d->waiting) + continueIncoming(); +} + +void SocksClient::continueIncoming() +{ + if(d->recvBuf.isEmpty()) + return; + + if(d->step == StepVersion) { + SPCS_VERSION s; + int r = spc_get_version(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + if(s.version != 0x05) { + reset(true); + error(ErrProxyNeg); + return; + } + + int methods = 0; + for(int n = 0; n < (int)s.methodList.size(); ++n) { + unsigned char c = s.methodList[n]; + if(c == 0x00) + methods |= AuthNone; + else if(c == 0x02) + methods |= AuthUsername; + } + d->waiting = true; + incomingMethods(methods); + } + } + else if(d->step == StepAuth) { + SPCS_AUTHUSERNAME s; + int r = spc_get_authUsername(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + d->waiting = true; + incomingAuth(s.user, s.pass); + } + } + else if(d->step == StepRequest) { + SPS_CONNREQ s; + int r = sp_get_request(&d->recvBuf, &s); + if(r == -1) { + reset(true); + error(ErrProxyNeg); + return; + } + else if(r == 1) { + d->waiting = true; + if(s.cmd == REQ_CONNECT) { + if(!s.host.isEmpty()) + d->rhost = s.host; + else + d->rhost = s.addr.toString(); + d->rport = s.port; + incomingConnectRequest(d->rhost, d->rport); + } + else if(s.cmd == REQ_UDPASSOCIATE) { + incomingUDPAssociateRequest(); + } + else { + requestDeny(); + return; + } + } + } +} + +void SocksClient::chooseMethod(int method) +{ + if(d->step != StepVersion || !d->waiting) + return; + + unsigned char c; + if(method == AuthNone) { + d->step = StepRequest; + c = 0x00; + } + else { + d->step = StepAuth; + c = 0x02; + } + + // version response + d->waiting = false; + writeData(sps_set_version(c)); + continueIncoming(); +} + +void SocksClient::authGrant(bool b) +{ + if(d->step != StepAuth || !d->waiting) + return; + + if(b) + d->step = StepRequest; + + // auth response + d->waiting = false; + writeData(sps_set_authUsername(b)); + if(!b) { + reset(true); + return; + } + continueIncoming(); +} + +void SocksClient::requestDeny() +{ + if(d->step != StepRequest || !d->waiting) + return; + + // response + d->waiting = false; + writeData(sp_set_request(d->rhost, d->rport, RET_UNREACHABLE)); + reset(true); +} + +void SocksClient::grantConnect() +{ + if(d->step != StepRequest || !d->waiting) + return; + + // response + d->waiting = false; + writeData(sp_set_request(d->rhost, d->rport, RET_SUCCESS)); + d->active = true; +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: server << Success >>\n"); +#endif + + if(!d->recvBuf.isEmpty()) { + appendRead(d->recvBuf); + d->recvBuf.resize(0); + readyRead(); + } +} + +void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort) +{ + if(d->step != StepRequest || !d->waiting) + return; + + // response + d->waiting = false; + writeData(sp_set_request(relayHost, relayPort, RET_SUCCESS)); + d->udp = true; + d->active = true; +#ifdef PROX_DEBUG + fprintf(stderr, "SocksClient: server << Success >>\n"); +#endif + + if(!d->recvBuf.isEmpty()) + d->recvBuf.resize(0); +} + +QHostAddress SocksClient::peerAddress() const +{ + return d->sock.peerAddress(); +} + +Q_UINT16 SocksClient::peerPort() const +{ + return d->sock.peerPort(); +} + +QString SocksClient::udpAddress() const +{ + return d->udpAddr; +} + +Q_UINT16 SocksClient::udpPort() const +{ + return d->udpPort; +} + +SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort) +{ + return new SocksUDP(this, host, port, routeAddr, routePort); +} + +//---------------------------------------------------------------------------- +// SocksServer +//---------------------------------------------------------------------------- +class SocksServer::Private +{ +public: + Private() {} + + ServSock serv; + QPtrList incomingConns; + QSocketDevice *sd; + QSocketNotifier *sn; +}; + +SocksServer::SocksServer(QObject *parent) +:QObject(parent) +{ + d = new Private; + d->sd = 0; + d->sn = 0; + connect(&d->serv, SIGNAL(connectionReady(int)), SLOT(connectionReady(int))); +} + +SocksServer::~SocksServer() +{ + stop(); + d->incomingConns.setAutoDelete(true); + d->incomingConns.clear(); + delete d; +} + +bool SocksServer::isActive() const +{ + return d->serv.isActive(); +} + +bool SocksServer::listen(Q_UINT16 port, bool udp) +{ + stop(); + if(!d->serv.listen(port)) + return false; + if(udp) { + d->sd = new QSocketDevice(QSocketDevice::Datagram); + d->sd->setBlocking(false); + if(!d->sd->bind(QHostAddress(), port)) { + delete d->sd; + d->sd = 0; + d->serv.stop(); + return false; + } + d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read); + connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int))); + } + return true; +} + +void SocksServer::stop() +{ + delete d->sn; + d->sn = 0; + delete d->sd; + d->sd = 0; + d->serv.stop(); +} + +int SocksServer::port() const +{ + return d->serv.port(); +} + +QHostAddress SocksServer::address() const +{ + return d->serv.address(); +} + +SocksClient *SocksServer::takeIncoming() +{ + if(d->incomingConns.isEmpty()) + return 0; + + SocksClient *c = d->incomingConns.getFirst(); + d->incomingConns.removeRef(c); + + // we don't care about errors anymore + disconnect(c, SIGNAL(error(int)), this, SLOT(connectionError())); + + // don't serve the connection until the event loop, to give the caller a chance to map signals + QTimer::singleShot(0, c, SLOT(serve())); + + return c; +} + +void SocksServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data) +{ + if(d->sd) { + d->sd->setBlocking(true); + d->sd->writeBlock(data.data(), data.size(), addr, port); + d->sd->setBlocking(false); + } +} + +void SocksServer::connectionReady(int s) +{ + SocksClient *c = new SocksClient(s, this); + connect(c, SIGNAL(error(int)), this, SLOT(connectionError())); + d->incomingConns.append(c); + incomingReady(); +} + +void SocksServer::connectionError() +{ + SocksClient *c = (SocksClient *)sender(); + d->incomingConns.removeRef(c); + c->deleteLater(); +} + +void SocksServer::sn_activated(int) +{ + QByteArray buf(8192); + int actual = d->sd->readBlock(buf.data(), buf.size()); + buf.resize(actual); + QHostAddress pa = d->sd->peerAddress(); + int pp = d->sd->peerPort(); + SPS_UDP s; + int r = sp_read_udp(&buf, &s); + if(r != 1) + return; + incomingUDP(s.host, s.port, pa, pp, s.data); +} + +// CS_NAMESPACE_END + +#include "socks.moc" -- cgit v1.2.1