summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp')
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp1223
1 files changed, 1223 insertions, 0 deletions
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<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qsocketdevice.h>
+#include<qsocketnotifier.h>
+
+#ifdef Q_OS_UNIX
+#include<sys/types.h>
+#include<netinet/in.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+#include"servsock.h"
+#include"bsocket.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#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<QObject> 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<SocksClient> 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"