#define EMS_PROXY_H
#pragma once
#include <WinSock2.h>
#include <string>
#include <vector>
using namespace std;
/*
sock代理工作原理大致如下:
1。[需要代理方]向服务器发出请求信息;
2。[代理方]应答;
3。[需要代理方]接到应答后发送向[代理方]发送目的ip和端口;
4。[代理方]与目的连接;
5。[代理方]将[需要代理方]发出的信息传到目的方,将目的方发出的信息传到[需要代理方];
6。代理完成。
sock5的TCP代理工作流程:
1。向服务器的代理端口建立tcp连接。一般为1080;
2。向服务器发送 05 02 00 02(此为16进制码,以下同),让代理服务器选择认证方式 ;
05
02 这里确认2种认证方式 无需认证和需要认证,只需要验证一种方式,可以直接发送05 01 00查询服务器是否支持无认证代理方式;
00 不需要认证;
02 需要认证;
3。如果接到 05 00 则是可以代理或则05 02需要认证,这里只需要判断第二字节就行;
如果需要认证,需要向服务器发送01 用户名长度(2字节)用户名 密码长度(2字节)密码,然后接收服务器返回数据,如果第二字节为 00,则认证通过,否则无法认证,则连接失败;
4。发送 05 01 00 01 + 目的地址(4字节)+ 目的端口(2字节),目的地址和端口都是16进制码(不是字符串)。
例202.103.190.27 - 7201
则发送的信息为:05 01 00 01 CA 67 BE 1B 1C 21
(CA=202 67=103 BE=190 1B=27 1C21=7201)
5。接收代理服务器返回的数据,我们只要判断第二字节是否为00即表示代理连接完成;
6。以后操作和直接与目的方进行TCP连接相同。
*/
enum ProxyStatus
{
SUCCESS,
CONNECT_PROXY_FAIL,
NOT_CONNECT_PROXY,
CONNECT_SERVER_FAIL
};
class CProxy
{
public:
CProxy():m_proxyType(0){
};
//构造函数的地址端口用户密码是代理服务器的
CProxy(long type, string ip, u_short port, string username, string password)
:m_proxyType(type), m_proxyIp(ip), m_proxyPort(port), m_proxyUserName(username), m_proxyUserPwd(password)
{}
void init(string ip, u_short port, string username, string password){
m_proxyIp = ip;
m_proxyPort = port;
m_proxyUserName = username;
m_proxyUserPwd = password;
};
~CProxy(void){};
ProxyStatus ConnectProxyServer(SOCKET socket);//先连接代理服务器
ProxyStatus ConnectServer(SOCKET socket, string ip, u_short port);//这里的ip,port是目标服务器
private:
ProxyStatus ConnectBySock5(SOCKET socket, string ip, u_short port);
bool Send(SOCKET socket, const char* buf, int len);
int Receive(SOCKET socket, char* buf, int bufLen);
private:
long m_proxyType;
string m_proxyIp;
u_short m_proxyPort;
string m_proxyUserName;
string m_proxyUserPwd;
bool m_blnProxyServerOk;
};
struct TSock5req1
{
char Ver; //版本
char nMethods; //方式
char Methods;
};
struct TSock5ans1
{
char Ver;
char Method;
};
struct TSock5req2
{
char Ver;
char Cmd;
char Rsv;
char Atyp;
char other;
};
struct TSock5ans2
{
char Ver;
char Rep;
char Rsv;
char Atyp;
char other;
};
struct TAuthreq
{
char Ver;
char Ulen;
char Name;
char PLen;
char Pass;
};
struct TAuthans
{
char Ver;
char Status;
};
#endif
#include "emsproxy.h"
#include <time.h>
#include <QtGlobal>
//先连接代理服务器
ProxyStatus CProxy::ConnectProxyServer(SOCKET socket)
{
int ret;
struct timeval timeout ;
fd_set r;
string ip;
u_short port;
ip = m_proxyIp;
port = m_proxyPort;
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
servAddr.sin_port = htons(port);
//设置非阻塞方式连接
unsigned long ul = 1;
ret = ioctlsocket(socket, FIONBIO, (unsigned long*)&ul);
if(ret == SOCKET_ERROR)
{
qDebug("ConnectProxyServer ioctlsocket failed.");
return CONNECT_PROXY_FAIL;
}
//连接代理服务器
connect(socket, (sockaddr*)&servAddr, sizeof(sockaddr));
FD_ZERO(&r);
FD_SET(socket, &r);
timeout.tv_sec = 5;
timeout.tv_usec =0;
ret = select(0, 0, &r, 0, &timeout);
if (ret <= 0)
{
m_blnProxyServerOk = false;
qDebug("ConnectProxyServer CONNECT_PROXY_FAIL");
return CONNECT_PROXY_FAIL;
}
else
{
m_blnProxyServerOk = true;
return SUCCESS;
}
}
//再连接目标服务器
ProxyStatus CProxy::ConnectServer(SOCKET socket, string ip, u_short port)
{
int ret;
int nTimeout;
if (!m_blnProxyServerOk)
{
return NOT_CONNECT_PROXY;
}
nTimeout = 5000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&nTimeout, sizeof(int));//设置接收超时
unsigned long ul = 0;
ret = ioctlsocket(socket, FIONBIO, (unsigned long*)&ul);//设置阻塞方式连接
switch(m_proxyType)
{
case 0: //SOCK5
return ConnectBySock5(socket, ip, port);
break;
default:
break;
}
return CONNECT_SERVER_FAIL;
}
ProxyStatus CProxy::ConnectBySock5(SOCKET socket, string ip, u_short port)
{
char buf[512];
struct TSock5req1 *proxyreq1;
proxyreq1 = (struct TSock5req1 *)buf;
proxyreq1->Ver = 5;
proxyreq1->nMethods = 1;
proxyreq1->Methods = m_proxyUserName != "" ? 2 : 0;
//请求认证方式
Send(socket, buf, 3);
struct TSock5ans1 *proxyans1;
proxyans1 = (struct TSock5ans1 *)buf;
memset(buf, 0, sizeof(buf));
Receive(socket, buf, sizeof(buf));
if(proxyans1->Ver != 5 || (proxyans1->Method != 0 && proxyans1->Method != 2))
{
qDebug("ConnectBySock5 CONNECT_SERVER_FAIL1");
return CONNECT_SERVER_FAIL;
}
//方式2为需要认证
if(proxyans1->Method == 2)
{
int nUserLen = m_proxyUserName.length();
int nPassLen = m_proxyUserPwd.length();
//struct TAuthreq *authreq;
//authreq = (struct TAuthreq *)buf;
//authreq->Ver = 1;
//authreq->Ulen = nUserLen;
//strcpy(authreq->Name, m_proxyUserName.c_str());
//authreq->PLen = nPassLen;
//strcpy(authreq->Pass, m_proxyUserPwd.c_str());
buf[0] = 1;
buf[1] = nUserLen;
memcpy(buf + 2, m_proxyUserName.c_str(), nUserLen);
buf[2 + nUserLen] = nPassLen;
memcpy(buf + 3 + nUserLen, m_proxyUserPwd.c_str(), nPassLen);
//认证用户密码
Send(socket, buf, 3 + nUserLen + nPassLen);
struct TAuthans *authans;
authans = (struct TAuthans *)buf;
memset(buf, 0, sizeof(buf));
Receive(socket, buf, sizeof(buf));
if(authans->Ver != 1 || authans->Status != 0)
{
qDebug("ConnectBySock5 CONNECT_SERVER_FAIL2");
return CONNECT_SERVER_FAIL;
}
}
memset(buf, 0, sizeof(buf));
struct TSock5req2 *proxyreq2;
proxyreq2 = (struct TSock5req2 *)buf;
proxyreq2->Ver = 5;
proxyreq2->Cmd = 1;
proxyreq2->Rsv = 0;
proxyreq2->Atyp = 1;
unsigned long tmpLong = inet_addr(ip.c_str());
unsigned short port1 = ntohs(port);
memcpy((char*)&proxyreq2->other, &tmpLong, 4);
memcpy((char*)(&proxyreq2->other) + 4, &port1, 2);
//Send(socket, buf, sizeof(struct TSock5req2) + 5);
//建立目标用户连接
Send(socket, buf, 10);
struct TSock5ans2 *proxyans2;
memset(buf ,0, sizeof(buf));
proxyans2 = (struct TSock5ans2 *)buf;
Receive(socket, buf, sizeof(buf));
if(proxyans2->Ver != 5 || proxyans2->Rep != 0)
{
qDebug("ConnectBySock5 CONNECT_SERVER_FAIL3");
return CONNECT_SERVER_FAIL;
}
return SUCCESS;
}
int CProxy::Receive(SOCKET socket, char* buf, int bufLen)
{
return recv(socket, buf, bufLen, 0);
}
bool CProxy::Send(SOCKET socket, const char* buf, int len)
{
long ilen = len;
int sendCnt = 0;
int ret;
while(sendCnt < ilen)
{
if((ret = send(socket, buf + sendCnt, ilen - sendCnt, 0)) == SOCKET_ERROR)
{
return false;
}
else
{
sendCnt += ret;
}
}
return true;
}