关于socks5代理的简单讲解
Socks5代理的请求和返回信息.
1.客户端发送到socks5代理至少三个字节的请求,第一个字节一定为5,第二个字节为使用多少种验证,第三个字节为验证模式代码. bitsCN.Com
例:如果要使用"USERNAME/PASSWORD",那么这三个字节为5 1 2
2.Sock5代理接到上面请求后,如果是支持"USERNAME/PASSORD"的验证模式,就会返回两个字节,第一字节为5,第二字节为2.
3.客户端然后就向socks5代理发送验证信息,信息第一字节不需要理会,第二字节为验证用户名的长度,第三字节开始是用户名和密码信息.
4.socks5代理验证用户名和密码成功后,向客户端返回两个字节,5和0,如果验证失败,返回0x05和0xff.
5.客户端开始向sock5代理发送第一个向远程目标进行操作的请求,请求模式如下:
第一位为5
第二位是使用模式,0x01代表TCP CONNECT,0x02代表TCP BIND,0x03代表UDP ASSOCIATE
第三位保留
第四位是地址使用模式:0x01代表IP V4地址;0x03代表域名;0x04代表IP V6地址(一般常见的只是0x01和0x03两种模式,因为很多软件都不支持IP V6的)
第五位开始就是目标的地址和端口.
6.socks代理开始处理这个请求,对于TCP CONNECT和UDP ASSOCIATE模式有不同的处理.
A.对于TCP CONNECT
将请求分析后,将目标地址和 目标端口从请求中解析出来(无论请求中带的地址是否以域名方式发送过来,最终要将地址转换为IPV4的地址),然后使用connect()连接到目标地址中的目标端口中去,如果成功连接,那就向客户端发送回10个字节的信息,第一字节为5,第二字节为0,第三字节为0,第四字节为1,其它字节都为0. 中国.网管联盟
B.对于UDP ASSOCIATE(这个复杂很多了)
将请求分析后,先保存好客户端的连接信息(客户端的IP和连接过来的源端口),然后本地创建一个UDP的socket,并将socket使用bind()绑入本地所有地址中的一个UDP端口中去,然后得到本地UDP绑定的IP和端口,创建一个10个字节的信息,返回给客户端去.第一字节为0x05,第二和第三字节都为0,第四字节为0x01(IPV4地址),第五位到第8位是UDP绑定的IP(以DWORD模式保存),第Array位和第10位是UDP绑定的端口(以WORD模式保存).
在这里,我们要详解了解的则是“0x02 用户名/密码”验证的过程。
当客户端发送带有0x02认证方法的报文(如:“0x05 0x01 0x02”)到服务端时,根据报文,服务端得知客户端支持用户名/密码认证(0x02),因此如果服务端需要验证,则发送“0x05 0x02”应答,这样客户端将会进入“用户名/密码”验证过程。
“0x02 用户名/密码”验证协议的报文格式是:
0x01 | 用户名长度(1字节)| 用户名(长度根据用户名长度域指定) | 口令长度(1字节) | 口令(长度由口令长度域指定)
所以报文的长度是根据用户名与密码的长度而定,比如以下报文:
0x01 0x02 0x41 0x42 0x02 0x43 0x43
则表示发送用户名为“AB”密码为“CC”的验证报文。
服务端接收到用户名/密码验证报文后进行相应处理并返回以下格式的应答报文:
0x01 | 验证结果标志
如果验证通过则“验证结果标志”的值为“0x00”,否则其它值都表示验证失败!不允许再进行下一步的操作。
这时socks5代理就验证了用户名laotse密码666888对不对啊,如果不对直接关闭连接就可不用反馈了。
如果这个用户名和密码通过了,可以进行代理,那么就发送01 00给客户端。那么下面就和匿名是一样的了,匿名就是省略了这一步而已。
代码示例
#include
#pragma comment(lib,"ws2_32.lib")
//用法相当于connect
int connect_proxy(SOCKET sock, const char *lpszDestHost, int nDestPort, const char * lpszProxyAddress, const int nProxyPort, BOOL bNeedAuth, const char * lpszUserName, const char * lpszPassword)
{
//connect to proxy
char szBuffer[1024 + 1] = { "" };
int nLen = 0;
SOCKADDR_IN saProxy;
saProxy.sin_family = AF_INET;
saProxy.sin_port = htons(nProxyPort);
saProxy.sin_addr.s_addr = inet_addr(lpszProxyAddress);
if (saProxy.sin_addr.S_un.S_addr == INADDR_NONE)
{
LPHOSTENT lphost;
lphost = gethostbyname(lpszProxyAddress);
if (lphost != NULL)
{
saProxy.sin_addr.S_un.S_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;
}
else
{
return -1;
}
}
//LSP层HOOK的时候才用的方式
//WSABUF DataBuf;
//char buffer[4];
//memset(buffer, 0, sizeof(buffer));
//DataBuf.len = 4;
//DataBuf.buf = buffer;
//int err = 0;
//if ((rc = NextProcTable.lpWSPConnect(s, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr), &DataBuf, NULL, NULL, NULL, &err)) == SOCKET_ERROR)
int nRet = -1;
if (oConnect(sock, (sockaddr*)&saProxy, sizeof(sockaddr)) == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
fd_set fdWrite;
FD_ZERO(&fdWrite);//初始化fd_set
FD_SET(sock, &fdWrite);
timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
nRet = select(0, NULL, &fdWrite, NULL, &tv);
if (nRet <= 0)
{
return -1;
}
}
else
{
return -1;
}
}
//auth
nLen = 0;
if (bNeedAuth)
{
szBuffer[0] = 5;
szBuffer[1] = 2;
szBuffer[2] = 0;
szBuffer[3] = 2;
nLen = 4;
send(sock, szBuffer, nLen, 0);
fd_set fdread;
FD_ZERO(&fdread);//初始化fd_set
FD_SET(sock, &fdread);
timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
nRet = select(0, &fdread, NULL, NULL, &tv);
if (nRet <= 0)
{
return -1;
}
nLen = 2;
recv(sock, szBuffer, nLen, 0);
if (szBuffer[0] == 5)
{
//need auth
if (szBuffer[1] == 2)
{
szBuffer[0] = 1;
nLen = strlen(lpszUserName);
szBuffer[1] = nLen;
strcpy(szBuffer + 2, lpszUserName);
nLen += 2;
szBuffer[nLen] = strlen(lpszPassword);
strcpy(szBuffer + nLen + 1, lpszPassword);
nLen = nLen + 1 + strlen(lpszPassword);
send(sock, szBuffer, nLen, 0);
nLen = 2;
fd_set fdread;
FD_ZERO(&fdread);//初始化fd_set
FD_SET(sock, &fdread);
timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
nRet = select(0, &fdread, NULL, NULL, &tv);
if (nRet <= 0)
{
return -1;
}
recv(sock, szBuffer, nLen, 0);
if (szBuffer[1] != 0)
{
return -1;//帐号或密码错误
}
}
else
{
if (szBuffer[1] != 0)
{
return -1;
}
}
}
else
{
return -1;
}
}
//进入发送代理请求的阶段
else
{
szBuffer[0] = 5;
szBuffer[1] = 1;
szBuffer[2] = 0;
nLen = 3;
send(sock, szBuffer, nLen, 0);
fd_set fdread;
FD_ZERO(&fdread);//初始化fd_set
FD_SET(sock, &fdread);
timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
nRet = select(0, &fdread, NULL, NULL, &tv);
if (nRet <= 0)
{
return -1;
}
nLen = 2;
recv(sock, szBuffer, nLen, 0);
if (szBuffer[0] != 5 || szBuffer[1] != 0)
{
return -1;
}
}
//translate DestAddr
szBuffer[0] = 5;
szBuffer[1] = 1;
szBuffer[2] = 0;
szBuffer[3] = 3;//DOMAIN
szBuffer[4] = strlen(lpszDestHost);//domain len
strcpy(szBuffer + 5, lpszDestHost);
unsigned short uPort = htons(nDestPort);
memcpy(szBuffer + 5 + strlen(lpszDestHost), &uPort, 2);
nLen = 5 + strlen(lpszDestHost) + 2;
send(sock, szBuffer, nLen, 0);
fd_set fdread;
FD_ZERO(&fdread);//初始化fd_set
FD_SET(sock, &fdread);
timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
nRet = select(0, &fdread, NULL, NULL, &tv);
if (nRet <= 0)
{
return -1;
}
nLen = 10;
recv(sock, szBuffer, nLen, 0);
if (szBuffer[0] != 5 || szBuffer[1] != 0)
{
return -1;
}
//以下为测试能否发送与返回数据的测试代码(测试无问题)
//char buffer[1024] = { 0 };
//sprintf(buffer, "GET / HTTP/1.0\nHost:www.baidu.com\n\n");
//int ret = send(s, buffer, strlen(buffer), 0);//send
//if (ret == SOCKET_ERROR)
//{
// closesocket(s);
// return rc;
//}
//while (1)
//{
// ZeroMemory(buffer, 1024);
// ret = recv(s, buffer, 1024, 0);//recv
// if (ret == SOCKET_ERROR)
// {
// fprintf(stderr, "recv() 函数错误 : %d\n", WSAGetLastError());
// break;
// }
// MessageBoxA(0, buffer, "", 0);
//}
return 0;
}
//其他例程:
TCP代理测试:
// socks5.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#pragma comment(lib, "WS2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);//指定版本号
::WSAStartup(sockVersion, &wsaData);//载入winsock的dll
//建立socket,基于tcp
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
printf("error");
::WSACleanup(); //释放资源
return 0;
}
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(1080);//端口号
servAddr.sin_addr.S_un.S_addr = inet_addr("192.168.50.103");//IP
//连接
if (::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf("error");
::WSACleanup(); //释放资源
return 0;
}
//发送链接请求
char request[10] = { 0x05,0x01,0x02 };
int ret = send(s, request, 3, 0);
char buff[156];//缓冲区
int nRecv = ::recv(s, buff, 156, 0);//接收数据
if (nRecv > 0)
{
buff[nRecv] = '\0';
}
//发送账户密码
char username[30] = "liujiayu";
char pwd[30] = "liujiayu";
char validatebuf[100] = { 0 };
validatebuf[0] = 0x01;
validatebuf[1] = strlen(username);
memcpy(validatebuf + 2, username, strlen(username));
validatebuf[2 + strlen(username)] = strlen(pwd);
memcpy(validatebuf + 3 + strlen(username), pwd, strlen(pwd));
ret = send(s, validatebuf, 3 + strlen(username) + strlen(pwd), 0);
nRecv = ::recv(s, buff, 156, 0);//接收数据
//发送远程目标连接
unsigned long l = inet_addr("124.115.16.165");
char dest[11] = { 0x05,0x01,0x00,0x01,0x7c,0x73,0x10,0xa5,0x00,0x50 };
ret = send(s, dest, 10, 0);
nRecv = ::recv(s, buff, 156, 0);//接收数据
char copntent[11] = "get";
ret = send(s, copntent, 4, 0);
nRecv = ::recv(s, buff, 156, 0);//接收数据
::closesocket(s); //关闭套接字
::WSACleanup(); //释放资源
return 0;
}
UDP代理测试:
// tt2.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#pragma comment(lib, "WS2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);//指定版本号
::WSAStartup(sockVersion, &wsaData);//载入winsock的dll
//建立socket,基于tcp
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
printf("error");
::WSACleanup(); //释放资源
return 0;
}
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(1080);//端口号
servAddr.sin_addr.S_un.S_addr = inet_addr("192.168.50.197");//IP
//连接
if (::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf("error");
::WSACleanup(); //释放资源
return 0;
}
//发送链接请求
char request[10] = { 0x05,0x01,0x02 };
int ret = send(s, request, 3, 0);
char buff[156];//缓冲区
int nRecv = ::recv(s, buff, 156, 0);//接收数据
if (nRecv > 0)
{
buff[nRecv] = '\0';
}
//发送账户密码
char username[30] = "liujiayu";
char pwd[30] = "liujiayu";
char validatebuf[100] = { 0 };
validatebuf[0] = 0x01;
validatebuf[1] = strlen(username);
memcpy(validatebuf + 2, username, strlen(username));
validatebuf[2 + strlen(username)] = strlen(pwd);
memcpy(validatebuf + 3 + strlen(username), pwd, strlen(pwd));
ret = send(s, validatebuf, 3 + strlen(username) + strlen(pwd), 0);
nRecv = ::recv(s, buff, 156, 0);//接收数据
//发送远程目标连接
unsigned long l = inet_addr("192.168.50.197");
char dest[10] = { 0x05,0x03,0x00,0x01,0xc0,0xa8,0x32,0xc5,0x00,0x5a };
ret = send(s, dest, 10, 0);
nRecv = ::recv(s, buff, 156, 0);//接收数据
unsigned short udpserverport = (buff[8] << 8) + buff[9];
SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.50.197");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(udpserverport);
char sendtobuf[30] = { 0x00,0x00,0x00,0x01,0xc0,0xa8,0x32,0xc5,0x04,0x38,0x31,0x11,0x11,0x11,0x11 };
int to = sendto(sockClient, sendtobuf, 16, 0, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
::closesocket(s); //关闭套接字
::WSACleanup(); //释放资源
return 0;
}