You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1224 lines
24 KiB
1224 lines
24 KiB
/*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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"
|