socks5理解

1.客户机连接到服务器,发送一个版本标识/方法选择报文

VER 1 NMETHODS 1 METHODS 1
x’05’ n(1-255) methons

mothons(选择n中)
X’00’ 无验证需求
X’01’ 通用安全服务应用程序接口(GSSAPI)
X’02’ 用户名/密码(USERNAME/PASSWORD)
X’03’ 至 X’7F’ IANA 分配(IANA ASSIGNED)
X’80’ 至 X’FE’ 私人方法保留(RESERVED FOR PRIVATE METHODS)
X’FF’ 无可接受方法(NO ACCEPTABLE METHODS)

2.服务器返回方法选择报文

ver1 methons1
x’05’ number

3.客户端发送请求的详细的报文

VER1 CMD1 RSV1 ATYP1 DST.ADDR n DST.PORT2
05 CONNECT X=‘01’, BIND= X’02’ , UDP ASSOCIATE =X’03’ 保留 IP V4 address: X’01’,DOMAINNAME: X’03’,IP V6 address: X’04’

4.服务器返回报文

VER 1 REP1 RSV1 ATYP1 BND.ADDRn BND.PORT2
05 o X’00’ succeeded o X’01’ general SOCKS server failure o X’02’ connection not allowed by ruleset o X’03’ Network unreachable o X’04’ Host unreachable o X’05’ Connection refused o X’06’ TTL expired o X’07’ Command not supported o X’08’ Address type not supported o X’09’ to X’FF’ unassigned X’00’ o IP V4 address: X’01’ o DOMAINNAME: X’03’ o IP V6 address: X’04’

BND.PORT和BND.ADDR域包含了欲连接主机的地址和端口号。

5.开始数据的传输

RSV FRAG ATYP DST.ADDR DST.PORT DATA
X’0000’ Current fragment number o IP V4 address: X’01’ o DOMAINNAME: X’03’ o IP V6 address: X’04’

附加知识

大小端
端:即数据的存储的顺序,好比int =123
大端的存储的方式:0x00=1,0x01=2,0x03=3
小端的储存方式:0x00=3,0x01=2,0x03=1
#!usr/bin/evn python
#! -*- coding:utf8 -*-
import socket
import threading
import struct
import select
from datetime import datetime
import base64

Server_addr = "127.0.0.1"
Server_port = 1080
Server_listen = 5
Auth = False


def make_server():
    """
    开启服务器,开始监听
    :return: 服务器
    """
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # server.setblocking(False)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 开启端口服用
    server.bind((Server_addr, Server_port))                       #
    server.listen(Server_listen)
    return server


def base64_encrypt(content):
    return base64.encodestring(content)


def base64_decrypt(content):
    return base64.decodestring(content)


def recv_argue(conn, size):
    """
    接收数据,最后判断数据是否接收完成
    :param conn: socket
    :param size: datas len
    :return: 接收到的数据datas
    """
    remain = size
    datas = []
    while remain > 0:
        data = conn.recv(remain)
        if len(data) == 0:
            raise Exception("Connection end.")
        remain -= len(data)
        datas.append(data)
    datas = "".join(datas)
    if len(datas) != size:
        raise Exception("Protocol error")
    return datas


def authenticate(conn, auth):
    """
    认证数据的正确性,用户名和密码,以及发送ver和mothod给client,让client知道相应的认证的方式
    :param conn:
    :param auth:
    :return:
    """
    req = recv_argue(conn, 3)  # 这里接受到的数据req装这客户端的所有可接受的认证方式,但是服务器看不看是他的事。。。。。
    if auth:
        # nedd auth
        conn.send("\x05\x02")
        checkUserPassword(conn)
    else:
        # don't need auth
        conn.send("\x05\x00")
    return conn


def checkUserPassword(conn):
    pass


def handleRemote(conn):
    """
    此处接受client的报文(ver,cmd,rsv,atyp,dst.addr,dst.port),
    处理报文,req[4]:0x01==ipv4,0x03==domainname,0x04==ipv6
    :param conn:
    :return:
    """
    req = recv_argue(conn, 5)
    if len(req) != 5:
        raise Exception("1.request error")
    if len(req) == 0:
        raise Exception("Connection end.")
    if ord(req[3]) == 1:
        addr_ip = recv_argue(conn, 4)
        addr = socket.inet_ntoa(addr_ip)
    elif ord(req[3]) == 3:
        addr_len = ord(req[4])
        addr = recv_argue(conn, int(addr_len))
    elif ord(req[3]) == 4:
        addr_ip = recv_argue(conn, 16)
        addr = socket.inet_ntop(socket.AF_INET6, addr_ip)
    else:
        raise Exception("addr type not support!")
    port = struct.unpack(">H", recv_argue(conn, 2))[0]    # port is 2 byte,因为python存储int为byte时有大小端的概念,所以...
    print datetime.now(),  addr, port
    remote = socket.create_connection((addr, port))
    return remote


def handleRequest(conn, remote):
    """
    服务器返回报文(ver,rep,rsv,atyp,bnd.addr,bnd.port),rep==0x00 is succeed ,else fail
    :param conn:
    :param remote:
    :return:
    """
    if remote:
        #success
        reply = "\x05\x00\x00\x01"
    else:
        #faile
        reply = "\x05\x01\x00\x01"
    reply += socket.inet_aton(Server_addr) + struct.pack(">H", Server_port)
    conn.send(reply)
    return conn


def send_all(sock, data):
    bytes_send = 0
    while True:
        res = sock.send(data[bytes_send:])
        if res < 0:
            return res
        bytes_send += res
        if bytes_send == len(data):
            return bytes_send


def sofineConnRemote(conn, remote):
    try:
        sockset = [conn, remote]
        while True:
            r, w, e = select.select(sockset, [], [])
            if conn in r:
                data = conn.recv(4096)
                if len(data) <= 0:
                    break
                res = send_all(remote, data)
                if res < len(data):
                    raise Exception("faile to send all data to remote")

            if remote in r:
                data = remote.recv(4096)
                if len(data) <= 0:
                    break
                res = send_all(conn, data)
                if res < len(data):
                    raise Exception("failed to send all data to conn")
    finally:
        conn.close()
        remote.close()


def sockets5_server(conn, tth):
    conn = authenticate(conn, Auth)  # 版本以及方法的选择,0x05是版本,Auth=0x00是无须认证,=0x02用户名以及密码
    remote = handleRemote(conn)
    conn = handleRequest(conn, remote)
    sofineConnRemote(conn, remote)


if __name__ == '__main__':
    server = make_server()
    tth = 0
    while True:
        conn, addr = server.accept()    # conn相当于一个建立好连接的socket
        tth += 1
        threading.Thread(target=sockets5_server, args=(conn, tth)).start()

整体的运行的过程

在这里插入图片描述

1.启动代理服务器proxy_server,一旦有client接入,启动ProxyThread线程函数
void StartProxy(u_short LisPort){ 
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData))return;
	SOCKET sProxy = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sProxy == SOCKET_ERROR)return;
	struct sockaddr_in Server = { 0 };

	Server.sin_family = AF_INET;
	Server.sin_addr.S_un.S_addr = INADDR_ANY;
	Server.sin_port = htons(LisPort);

	if (bind(sProxy, (LPSOCKADDR)&Server, sizeof(Server)) == SOCKET_ERROR)return;

	if (listen(sProxy, SOMAXCONN) == SOCKET_ERROR)return;

	while (1){
		SOCKET sClient = accept(sProxy, NULL, NULL);
		HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ProxyThread, (PVOID)sClient, 0, NULL);
		if (hThread)
			CloseHandle(hThread);
	}

	closesocket(sProxy);
	WSACleanup();
}
2. client_socket这个链接发送数据(ver,cmd,rsv,atyp,ip_len,ip_info),proxy_server与其进行协商
//1.进行代理类型的判断
DWORD WINAPI ProxyThread(PVOID sClient){
	SOCKET CSsocket[2];
	CSsocket[0] = (SOCKET)sClient;
	CSsocket[1] = NULL;

	char buf[1024];
	memset(buf, 0, sizeof(buf));

	int DataLen = recv(CSsocket[0], buf, sizeof(buf), 0);
	if (DataLen < 3)
		goto exit;

	char ProxyType = buf[0];
	if (ProxyType == 5)
	{
		if (!DoSocks5(CSsocket, buf))
			goto exit;
	}
	else if (ProxyType == 4)
	{
		Socks4Req *Socks4Request = (Socks4Req *)buf;
		IP_PORT IPP;
		IPP.Port = Socks4Request->wPort;

		if (buf[4] != 0x00) //USERID !!
			IPP.IP = Socks4Request->dwIP;
		else
		{
			HOSTENT *hostent = gethostbyname((char*)&Socks4Request->other + 1);
			if (hostent == NULL)
				goto exit;

			IPP.IP = **(PULONG*)hostent->h_addr_list;
		}

		memset(Socks4Request, 0, 9);
		CSsocket[1] = ConnectToRemoteIP(&IPP);
		if (CSsocket[1])
			Socks4Request->REP = 0x5A; //GRANT  准许
		else
			Socks4Request->REP = 0x5B; //REJECT 拒绝

		if (send(CSsocket[0], (char *)Socks4Request, 8, 0) == SOCKET_ERROR)
			goto exit;

		if (Socks4Request->REP == 0x5B)
			goto exit;
	}
	else
	{
		if (!HttpProxy(CSsocket, buf, DataLen))
			goto exit;
	}

	if (CSsocket[0] && CSsocket[1])
		TCPTransfer(CSsocket);

exit:
	if (CSsocket[1])
		closesocket(CSsocket[1]);
	if (CSsocket[0])
		closesocket(CSsocket[0]);

	return 0;
}
//2.proxy_server回应client  (ver,nmethod)
BOOL DoSocks5(SOCKET *CSsocket, char *ReceiveBuf){
	if (!Authentication(CSsocket[0], ReceiveBuf))
		goto exit;

	Socks5Reply SAC;
	SAC.Ver = 0x05;
	SAC.REP = 0x01;  // 拒绝
	SAC.RSV = 0x00;
	SAC.ATYP = 0x01;

	IP_PORT IP_Port;
	int CMD = Get_IP_Port(CSsocket[0], ReceiveBuf, &IP_Port);
	if (!CMD)	goto exit;
	else if (CMD == 1) {//TCP CONNECT
		CSsocket[1] = ConnectToRemoteIP(&IP_Port);
		if (CSsocket[1])	SAC.REP = 0x00;
		if (send(CSsocket[0], (char *)&SAC, 10, 0) == SOCKET_ERROR)goto exit;
		if (SAC.REP == 0x01)goto exit;
		return 1;
	}
	else if (CMD == 3) {//UDP ASSOCIATE
		Socks5UDP S5UDP;
		std::memset(&S5UDP, 0, sizeof(Socks5UDP));

		struct sockaddr_in in;
		std::memset(&in, 0, sizeof(sockaddr_in));
		int structsize = sizeof(sockaddr_in);
		getpeername(CSsocket[0], (struct sockaddr *)&in, &structsize);  //Save the client connection information(client IP and source port)
		S5UDP.Client.socks = CSsocket[0];
		S5UDP.Client.IP_Port.IP = in.sin_addr.s_addr;
		S5UDP.Client.IP_Port.Port = in.sin_port;
		if (CreateUDPSocket(&SAC, &S5UDP.Local.socks))SAC.REP = 0x00;
		if (SAC.REP == 0x01)	goto exit;
		if (send(CSsocket[0], (char *)&SAC, 10, 0) == SOCKET_ERROR)	goto exit;
		S5UDP.Local.IP_Port = SAC.IP_PORT;
		UDPTransfer(&S5UDP);
	}

exit:
	return 0;
}
3.其中2回应packet(ver,methods)时,看是否存在用户认证,如果存在认证,则先接收认证的数据,如通过则回应成功,如果不需要进行认证,直接回应成功
int Authentication(SOCKET s, char *buf) { // done!
	Socks5Req *sq = (Socks5Req *)buf;

	char SendBuf[2] = { 5, 0 };
	//1.response packet(ver,methods)
	if ((sq->Methods[0] == 0) || (sq->Methods[0] == 2)){  // 0 : 无需认证;1 : GSSAPI;2 : 需要用户名和PASSWORD
		if (strlen(g_Username) == 0)	SendBuf[1] = 0x00;
		else SendBuf[1] = 0x02;
		if (send(s, SendBuf, 2, 0) == SOCKET_ERROR)return 0;
	}
	else return 0;

	//2. start to authentication
	//now buf= (version + ulen + data[user_name + plen + password])
	if (SendBuf[1] == 2){
		char USER[256] = {0};
		char PASS[256] = {0};
		int DataLen = recv(s, buf, 1024, 0);
		if (DataLen == SOCKET_ERROR || DataLen == 0)return 0;
	
		AuthReq *aq = (AuthReq *)buf;
		if (aq->Ver != 1)return 0;
	

		if ((aq->Ulen != 0) && (aq->Ulen <= 256))
			memcpy(USER, buf + 2, aq->Ulen);

		int PLen = buf[2 + aq->Ulen];
		if ((PLen != 0) && (PLen <= 256))
			memcpy(PASS, buf + 3 + aq->Ulen, PLen);

		if (!strcmp(g_Username, USER) && !strcmp(g_Password, PASS))	buf[1] = 0;
		else 	buf[1] = -1;
		//3.response packet to client(ver,result)====(1,0) is authentication success else  is fail
		if (send(s, buf, 2, 0) == SOCKET_ERROR)return 0;
	}

	return 1;
}
4.proxy_server回应client(ver,rep,rsv,atyp,ip,port)
5.进行数据的转发
void TCPTransfer(SOCKET* CSsocket){
	int result;
	SOCKET ClientSocket = CSsocket[0];
	SOCKET ServerSocket = CSsocket[1];
	struct timeval timeset;
	fd_set readfd, writefd;

	char SenderBuf[MAXBUFSIZE];
	char read_in1[MAXBUFSIZE], send_out1[MAXBUFSIZE];
	char read_in2[MAXBUFSIZE], send_out2[MAXBUFSIZE];

	int read1 = 0, totalread1 = 0, send1 = 0;
	int read2 = 0, totalread2 = 0, send2 = 0;
	int sendcount1, sendcount2;

	int maxfd = (int)(max(ClientSocket, ServerSocket)) + 1;
	int i = 0;

	memset(read_in1, 0, MAXBUFSIZE);
	memset(read_in2, 0, MAXBUFSIZE);
	memset(send_out1, 0, MAXBUFSIZE);
	memset(send_out2, 0, MAXBUFSIZE);

	timeset.tv_sec = TIMEOUT;
	timeset.tv_usec = 0;
	while (1)
	{
		FD_ZERO(&readfd);
		FD_ZERO(&writefd);

		FD_SET((UINT)ClientSocket, &readfd);
		FD_SET((UINT)ClientSocket, &writefd);
		FD_SET((UINT)ServerSocket, &writefd);
		FD_SET((UINT)ServerSocket, &readfd);

		result = select(maxfd, &readfd, &writefd, NULL, &timeset);
		if ((result<0) && (errno != EINTR))
		{
			printf("Select error.\r\n");
			break;
		}
		else if (result == 0)
		{
			printf("Socket time out.\r\n");
			break;
		}
		if (FD_ISSET(ServerSocket, &readfd))
		{
			if (totalread2<MAXBUFSIZE)
			{
				read2 = recv(ServerSocket, read_in2, MAXBUFSIZE - totalread2, 0);
				if (read2 == 0)break;
				if ((read2<0) && (errno != EINTR))
				{
					printf("Read ServerSocket data error,maybe close?\r\n\r\n");
					break;
				}

				memcpy(send_out2 + totalread2, read_in2, read2);

				totalread2 += read2;
				memset(read_in2, 0, MAXBUFSIZE);
			}
		}

		if (FD_ISSET(ClientSocket, &writefd)){
			int err2 = 0;
			sendcount2 = 0;
			while (totalread2>0){
				send2 = send(ClientSocket, send_out2 + sendcount2, totalread2, 0);
				if (send2 == 0)break;
				if ((send2<0) && (errno != EINTR))
				{
					printf("Send to ClientSocket unknow error.\r\n");
					err2 = 1;
					break;
				}
				if ((send2<0) && (errno == ENOSPC)) break;
				sendcount2 += send2;
				totalread2 -= send2;

			}//end while 

			if (err2 == 1) break;
			if ((totalread2>0) && (sendcount2 > 0))
			{
				/* move not sended data to start addr */
				memcpy(send_out2, send_out2 + sendcount2, totalread2);
				memset(send_out2 + totalread2, 0, MAXBUFSIZE - totalread2);
			}
			else
				memset(send_out2, 0, MAXBUFSIZE);
		}


		if (FD_ISSET(ClientSocket, &readfd))
		{
			if (totalread1<MAXBUFSIZE)
			{
				read1 = recv(ClientSocket, read_in1, MAXBUFSIZE - totalread1, 0);
				if ((read1 == SOCKET_ERROR) || (read1 == 0))
				{
					break;
				}

				memcpy(send_out1 + totalread1, read_in1, read1);

				totalread1 += read1;
				memset(read_in1, 0, MAXBUFSIZE);
			}
			if (SendRequest(CSsocket, SenderBuf, send_out1, totalread1))
				totalread1 = 0;
		}

		if (FD_ISSET(ServerSocket, &writefd))
		{
			int err = 0;
			sendcount1 = 0;
			while (totalread1>0)
			{
				send1 = send(ServerSocket, send_out1 + sendcount1, totalread1, 0);
				if (send1 == 0)break;
				if ((send1<0) && (errno != EINTR))
				{
					err = 1;
					break;
				}

				if ((send1<0) && (errno == ENOSPC)) break;
				sendcount1 += send1;
				totalread1 -= send1;

			}

			if (err == 1) break;
			if ((totalread1>0) && (sendcount1>0))
			{
				memcpy(send_out1, send_out1 + sendcount1, totalread1);
				memset(send_out1 + totalread1, 0, MAXBUFSIZE - totalread1);
			}
			else
				memset(send_out1, 0, MAXBUFSIZE);
		}
		Sleep(5);
	}

	closesocket(ClientSocket);
	closesocket(ServerSocket);
}
6.当然涉及的域名解析,和UDP转发等很多,附具体的代码

#include 
#include 
#include 
#include 

#pragma comment(lib,"ws2_32.lib")

using namespace std;

#define MAX_HOSTNAME 256
#define MAXBUFSIZE   20480
#define TIMEOUT      10000
#define HEADLEN      7

#define _OUT_

char HTTP_200_OK[] = "HTTP/1.0 200 OK\r\n\r\n";

char g_Username[256];
char g_Password[256];

struct Socks4Req
{
	BYTE Ver;
	BYTE REP;
	WORD wPort;
	DWORD dwIP;
	BYTE other[1];
};

struct Socks5Req
{
	BYTE Ver;
	BYTE nMethods;
	BYTE Methods[255];
};

struct AuthReq
{
	BYTE Ver;
	BYTE Ulen;
	BYTE UserPass[1024];
};

typedef struct
{
	BYTE Ver;      // Version Number
	BYTE CMD;      // 0x01==TCP CONNECT,0x02==TCP BIND,0x03==UDP ASSOCIATE
	BYTE RSV;
	BYTE ATYP;
	BYTE IP_LEN;
	BYTE szIP;
}Socks5Info;

typedef struct
{
	DWORD IP;
	WORD Port;
}IP_PORT;

typedef struct
{
	BYTE Ver;
	BYTE REP;
	BYTE RSV;
	BYTE ATYP;
	IP_PORT IP_PORT;
}Socks5Reply;

typedef struct
{
	BYTE RSV[2];
	BYTE FRAG;
	BYTE ATYP;
	IP_PORT IP_PORT;
	// BYTE DATA;
}Socks5UDPHead;

struct SocketInfo
{
	SOCKET socks;
	IP_PORT IP_Port;
};

typedef struct
{
	SocketInfo Local;
	SocketInfo Client;
	SocketInfo Server;
}Socks5UDP;
// End Of Structure

void TCPTransfer(SOCKET* CSsocket);
void UDPTransfer(Socks5UDP *sPara);
BOOL ConnectToRemoteHost(SOCKET *ServerSocket, char *HostName, const WORD RemotePort);

//functions

void GetHostNameAndPort(char *ReceiveBuf, int datalen, char *HostName, UINT *RemotePort){
	//得到相应的域名和port
	if (datalen > MAX_HOSTNAME)datalen = MAX_HOSTNAME;
	char *p = ReceiveBuf;
	for (int i = 0;i < datalen && *p != ':' && *p != '\0' && *p != '\r' && *p != '/';i++){
		HostName[i] = *p++;
		if (*p == ':')*RemotePort = atoi(p + 1);
	}
}
//---------------------------------------------------------------------------
char * GetURLRootPoint(char * ReceiveBuf, int DataLen, int *HostNaneLen)
{
	for (int i = 0; i < DataLen; i++)
	{
		if (ReceiveBuf[i] == '/')
		{
			*HostNaneLen = i;
			return &ReceiveBuf[i];
		}
	}
	return NULL;
}
//---------------------------------------------------------------------------
int CheckHttpRequest(const char *ReceiveBuf, int *MethodLength){
	if (!_strnicmp(ReceiveBuf, "GET ", 4)){
		*MethodLength = 4;
		return 1;
	}

	if (!_strnicmp(ReceiveBuf, "HEAD ", 5)) //Looks like the same with GET
	{
		*MethodLength = 5;
		return 2;
	}

	if (!_strnicmp(ReceiveBuf, "POST ", 5))
	{
		*MethodLength = 5;
		return 3;
	}

	if (!_strnicmp(ReceiveBuf, "CONNECT ", 8))
	{
		*MethodLength = 8;
		return 4;
	}

	return 0;
}

int ModifyRequest(char *SenderBuf, char *ReceiveBuf, int DataLen, int MethodLength)
{
	strncpy_s(SenderBuf, MAXBUFSIZE, ReceiveBuf, MethodLength);

	if (strncmp(ReceiveBuf + MethodLength, "http://", HEADLEN))
		return 0;

	int HedLen = 0;
	char * Getrootfp = GetURLRootPoint(ReceiveBuf + MethodLength + HEADLEN, DataLen - MethodLength - HEADLEN, &HedLen);
	if (Getrootfp == NULL)
		return 0;

	memcpy(SenderBuf + MethodLength, Getrootfp, DataLen - MethodLength - HEADLEN - HedLen);

	return DataLen - HEADLEN - HedLen;
}

BOOL HttpProxy(SOCKET* CSsocket, char *ReceiveBuf, int DataLen)
{
	int MethodLength;
	int Flag = CheckHttpRequest(ReceiveBuf, &MethodLength);
	if (!Flag)
		goto exit;

	char *SenderBuf = (char*)malloc(MAXBUFSIZE);
	if (SenderBuf)
	{
		memset(SenderBuf, 0, MAXBUFSIZE);

		char HostName[MAX_HOSTNAME] = { 0 };
		UINT RemotePort = 80;

		if (Flag == 1 || Flag == 2 || Flag == 3)
		{
			int SendLength = ModifyRequest(SenderBuf, ReceiveBuf, DataLen, MethodLength);
			if (!SendLength)
				return 0;

			GetHostNameAndPort(ReceiveBuf + MethodLength + HEADLEN,
				DataLen - MethodLength - HEADLEN,
				HostName,
				&RemotePort);

			if (!ConnectToRemoteHost(&CSsocket[1], HostName, RemotePort))
				return 0;

			if (send(CSsocket[1], SenderBuf, SendLength, 0) == SOCKET_ERROR)
				return 0;
		}
		else if (Flag == 4)
		{
			GetHostNameAndPort(ReceiveBuf + MethodLength,
				DataLen - MethodLength,
				HostName,
				&RemotePort);

			if (!ConnectToRemoteHost(&CSsocket[1], HostName, RemotePort))
				return 0;

			send(CSsocket[0], HTTP_200_OK, (int)strlen(HTTP_200_OK) + 1, 0);
		}

		free(SenderBuf);
		return 1;
	}

exit:
	return 0;
}

BOOL SendRequest(SOCKET* CSsocket, char *SenderBuf, char *ReceiveBuf, int DataLen){
	int MethodLength = 0;
	int Flag = CheckHttpRequest(ReceiveBuf, &MethodLength);
	if (Flag == 0)
		return 0;

	char HostName[MAX_HOSTNAME] = { 0 };
	UINT RemotePort = 80;

	if (Flag == 1 || Flag == 2 || Flag == 3){
		int SendLength = ModifyRequest(SenderBuf, ReceiveBuf, DataLen, MethodLength);
		if (!SendLength)return 0;
		//现在sendrbuf=methon+/data 原来的接收到的数据(method+http://domain/data)
		GetHostNameAndPort(ReceiveBuf + MethodLength + HEADLEN, DataLen - MethodLength - HEADLEN, HostName, &RemotePort);
		if (!ConnectToRemoteHost(&CSsocket[1], HostName, RemotePort))return 0;
		//已经连接上远程的服务器,传输数据过去
		if (send(CSsocket[1], SenderBuf, SendLength, 0) == SOCKET_ERROR)return 0;
	}
	else if (Flag == 4){
		GetHostNameAndPort(ReceiveBuf + MethodLength,DataLen - MethodLength,HostName,&RemotePort);
		if (!ConnectToRemoteHost(&CSsocket[1], HostName, RemotePort))return 0;

		send(CSsocket[0], HTTP_200_OK, (int)strlen(HTTP_200_OK) + 1, 0);
	}

	//if (CSsocket[0] && CSsocket[1]){
	//	//printf("HTTP Proxy request OK.\n");
	//	HANDLE ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TCPTransfer, (LPVOID)CSsocket, 0, 0);
	//	if (ThreadHandle)WaitForSingleObject(ThreadHandle, INFINITE);
	//}
	//else
	//	return 0;

	return 1;
}

int Authentication(SOCKET s, char *buf) { // done!
	Socks5Req *sq = (Socks5Req *)buf;

	char SendBuf[2] = { 5, 0 };
	//1.response packet(ver,methods)
	if ((sq->Methods[0] == 0) || (sq->Methods[0] == 2)){  // 0 : 无需认证;1 : GSSAPI;2 : 需要用户名和PASSWORD
		if (strlen(g_Username) == 0)	SendBuf[1] = 0x00;
		else SendBuf[1] = 0x02;
		if (send(s, SendBuf, 2, 0) == SOCKET_ERROR)return 0;
	}
	else return 0;

	//2. start to authentication
	//now buf= (version + ulen + data[user_name + plen + password])
	if (SendBuf[1] == 2){
		char USER[256] = {0};
		char PASS[256] = {0};
		int DataLen = recv(s, buf, 1024, 0);
		if (DataLen == SOCKET_ERROR || DataLen == 0)return 0;
	
		AuthReq *aq = (AuthReq *)buf;
		if (aq->Ver != 1)return 0;
	

		if ((aq->Ulen != 0) && (aq->Ulen <= 256))
			memcpy(USER, buf + 2, aq->Ulen);

		int PLen = buf[2 + aq->Ulen];
		if ((PLen != 0) && (PLen <= 256))
			memcpy(PASS, buf + 3 + aq->Ulen, PLen);

		if (!strcmp(g_Username, USER) && !strcmp(g_Password, PASS))	buf[1] = 0;
		else 	buf[1] = -1;
		//3.response packet to client(ver,result)====(1,0) is authentication success else  is fail
		if (send(s, buf, 2, 0) == SOCKET_ERROR)return 0;
	}

	return 1;
}

ULONG DNS(char *HostName)
{
	HOSTENT *hostent = gethostbyname(HostName);
	if (hostent == NULL)
		return 0;

	return **(PULONG*)hostent->h_addr_list;
}

int GetAddressAndPort(char *ReceiveBuf, int DataLen, char *HostName, ULONG *pIp, WORD *RemotePort)
{
	Socks5Info *Socks5Request = (Socks5Info *)ReceiveBuf;

	if ((Socks5Request->Ver == 0) && (Socks5Request->CMD == 0))
	{
		if (Socks5Request->ATYP == 1)       // IPv4
		{
			IP_PORT *IPP = (IP_PORT *)&Socks5Request->IP_LEN;
			*pIp = IPP->IP;
			*RemotePort = IPP->Port;
			return 10;                       //return Data Enter point
		}
		else if (Socks5Request->ATYP == 3)  // 域名
		{
			memcpy(HostName, &Socks5Request->szIP, Socks5Request->IP_LEN);
			memcpy(RemotePort, &Socks5Request->szIP + Socks5Request->IP_LEN, 2);
			return 7 + Socks5Request->IP_LEN;  //return Data Enter point
		}

		return 1;
	}

	return 0;
}

SOCKET ConnectToRemoteIP(IP_PORT *pIPP)
{
	// Create Socket
	SOCKET ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (ServerSocket == INVALID_SOCKET)
		return NULL;

	struct sockaddr_in Server;
	memset(&Server, 0, sizeof(Server));

	Server.sin_family = AF_INET;
	Server.sin_addr.s_addr = pIPP->IP;
	Server.sin_port = pIPP->Port;

	UINT TimeOut = TIMEOUT;
	setsockopt(ServerSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut));
	if (connect(ServerSocket, (const SOCKADDR *)&Server, sizeof(Server)) == SOCKET_ERROR)
	{
		UCHAR *p = (UCHAR *)&pIPP->IP;
		printf("Fail To Connect To Remote Host : %d.%d.%d.%d:%d\n",
			p[0], p[1], p[2], p[3], ntohs(pIPP->Port));
		closesocket(ServerSocket);
		return NULL;
	}

	return ServerSocket;
}

BOOL ConnectToRemoteHost(SOCKET *ServerSocket, char *HostName, const WORD RemotePort)
{
	struct sockaddr_in Server;
	memset(&Server, 0, sizeof(Server));

	Server.sin_family = AF_INET;
	Server.sin_port = htons(RemotePort);

	if (inet_addr(HostName) != INADDR_NONE)
		Server.sin_addr.s_addr = inet_addr(HostName);
	else
		Server.sin_addr.s_addr = DNS(HostName);

	// Create Socket
	*ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (*ServerSocket == INVALID_SOCKET)
		return FALSE;

	UINT TimeOut = TIMEOUT;
	setsockopt(*ServerSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut));
	if (connect(*ServerSocket, (const SOCKADDR *)&Server, sizeof(Server)) == SOCKET_ERROR)
	{
		printf("Fail To Connect To Remote Host\n");
		closesocket(*ServerSocket);
		return FALSE;
	}

	return TRUE;
}


int Get_IP_Port(SOCKET s, char *ReceiveBuf, IP_PORT *IPP){
	//1.request packet from client is (ver,cmd,rsv,atyp,dst.addr,dst.port)
	int DataLen = recv(s, ReceiveBuf, 1024, 0);
	if (DataLen == SOCKET_ERROR || DataLen == 0) return 0;

	Socks5Info *Socks5Request = (Socks5Info *)ReceiveBuf;

	// ATYP : 0x01==IPv4 地址;		 0x03==域名;		0x04==IPv6地址 - not Support
	if (Socks5Request->ATYP == 1){
		*IPP = *(IP_PORT *)&Socks5Request->IP_LEN;
	}
	else if (Socks5Request->ATYP == 3){
		IPP->Port = *(WORD*)((char*)&Socks5Request->szIP + Socks5Request->IP_LEN);
		//cout << (int)IPP->Port << endl;
		if (Socks5Request->IP_LEN >= MAX_HOSTNAME) return 0;

		char HostName[MAX_HOSTNAME];
		memcpy(HostName, (char*)&Socks5Request->szIP, Socks5Request->IP_LEN);

		HostName[Socks5Request->IP_LEN] = 0;
		HOSTENT *hostent = gethostbyname(HostName);
		if (hostent == NULL) return 0;
		IPP->IP = **(PULONG*)hostent->h_addr_list;
	}
	else
		return 0;

	// Get and return the work mode. 1:TCP CONNECT; 3:UDP ASSOCIATE
	if ((Socks5Request->CMD == 1) || (Socks5Request->CMD == 3))
		return Socks5Request->CMD;

	return 0;
}

BOOL CreateUDPSocket(_OUT_ Socks5Reply *SAC, _OUT_ SOCKET *p_sock)
{
	*p_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (*p_sock == SOCKET_ERROR)
		return 0;

	struct sockaddr_in UDPServer;
	UDPServer.sin_family = AF_INET;
	UDPServer.sin_addr.s_addr = INADDR_ANY;
	UDPServer.sin_port = INADDR_ANY;

	if (bind(*p_sock, (SOCKADDR*)&UDPServer, sizeof(UDPServer)) == SOCKET_ERROR)
	{
		printf("UDP socket bind failed.\n");
		return 0;
	}

	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(sockaddr_in));
	int AddrLen = sizeof(sockaddr_in);
	getsockname(*p_sock, (struct sockaddr *)&addr, &AddrLen);
	SAC->IP_PORT.IP = addr.sin_addr.s_addr;
	SAC->IP_PORT.Port = addr.sin_port;
	printf("UDP port : %d\n", ntohs(addr.sin_port));

	return 1;
}

BOOL DoSocks5(SOCKET *CSsocket, char *ReceiveBuf){
	if (!Authentication(CSsocket[0], ReceiveBuf))
		goto exit;

	Socks5Reply SAC;
	SAC.Ver = 0x05;
	SAC.REP = 0x01;  // 拒绝
	SAC.RSV = 0x00;
	SAC.ATYP = 0x01;

	IP_PORT IP_Port;
	int CMD = Get_IP_Port(CSsocket[0], ReceiveBuf, &IP_Port);
	if (!CMD)	goto exit;
	else if (CMD == 1) {//TCP CONNECT
		CSsocket[1] = ConnectToRemoteIP(&IP_Port);
		if (CSsocket[1])	SAC.REP = 0x00;
		if (send(CSsocket[0], (char *)&SAC, 10, 0) == SOCKET_ERROR)goto exit;
		if (SAC.REP == 0x01)goto exit;
		return 1;
	}
	else if (CMD == 3) {//UDP ASSOCIATE
		Socks5UDP S5UDP;
		std::memset(&S5UDP, 0, sizeof(Socks5UDP));

		struct sockaddr_in in;
		std::memset(&in, 0, sizeof(sockaddr_in));
		int structsize = sizeof(sockaddr_in);
		getpeername(CSsocket[0], (struct sockaddr *)&in, &structsize);  //Save the client connection information(client IP and source port)
		S5UDP.Client.socks = CSsocket[0];
		S5UDP.Client.IP_Port.IP = in.sin_addr.s_addr;
		S5UDP.Client.IP_Port.Port = in.sin_port;
		if (CreateUDPSocket(&SAC, &S5UDP.Local.socks))SAC.REP = 0x00;
		if (SAC.REP == 0x01)	goto exit;
		if (send(CSsocket[0], (char *)&SAC, 10, 0) == SOCKET_ERROR)	goto exit;
		S5UDP.Local.IP_Port = SAC.IP_PORT;
		UDPTransfer(&S5UDP);
	}

exit:
	return 0;
}

int UDPSend(SOCKET s, char *buf, int nBufSize, struct sockaddr_in *to, int tolen)
{
	int nBytesLeft = nBufSize;
	int nBytes = 0;

	while (nBytesLeft > 0)
	{
		nBytes = sendto(s, &buf[nBufSize - nBytesLeft], nBytesLeft, 0, (SOCKADDR *)to, tolen);
		if (nBytes == SOCKET_ERROR)
		{
			//printf("Failed to send buffer to socket %d.\r\n", WSAGetLastError());
			return SOCKET_ERROR;
		}
		nBytesLeft -= nBytes;
	}

	return nBufSize - nBytesLeft;
}

void UDPTransfer(Socks5UDP *pS5UDP)
{
	int    result;
	struct sockaddr_in SenderAddr;
	int    SenderAddrSize = sizeof(SenderAddr);
	char   RecvBuf[MAXBUFSIZE];

	struct sockaddr_in UDPClient, UDPServer;
	memset(&UDPClient, 0, sizeof(sockaddr_in));
	memset(&UDPServer, 0, sizeof(sockaddr_in));

	UDPClient.sin_family = AF_INET;
	UDPClient.sin_addr.s_addr = pS5UDP->Client.IP_Port.IP;

	fd_set readfd;
	int DataLength = 0;
	while (1)
	{
		FD_ZERO(&readfd);
		FD_SET((UINT)pS5UDP->Local.socks, &readfd);
		FD_SET((UINT)pS5UDP->Client.socks, &readfd);
		result = select((int)pS5UDP->Local.socks + 1, &readfd, NULL, NULL, NULL);
		if ((result<0) && (errno != EINTR))
		{
			//printf("Select error.\r\n");
			break;
		}
		if (FD_ISSET(pS5UDP->Client.socks, &readfd))
			break;
		if (FD_ISSET(pS5UDP->Local.socks, &readfd))
		{
			memset(RecvBuf, 0, MAXBUFSIZE);
			DataLength = recvfrom(pS5UDP->Local.socks,
				RecvBuf + 10, MAXBUFSIZE - 10, 0, (struct sockaddr FAR *)&SenderAddr, &SenderAddrSize);
			if (DataLength == SOCKET_ERROR)
			{
				//printf("UDPTransfer recvfrom error.\n");
				break;
			}

			if (SenderAddr.sin_addr.s_addr == pS5UDP->Client.IP_Port.IP) //Data come from client
			{
				// // send data to UDP server
				char HostName[MAX_HOSTNAME] = { 0 };
				ULONG ip = 0;
				WORD RemotePort = 0;

				int DataOffset = GetAddressAndPort(RecvBuf + 10, DataLength, HostName, &ip, &RemotePort);
				if (DataOffset)
				{
					UDPServer.sin_family = AF_INET;
					if (ip)
						UDPServer.sin_addr.s_addr = ip;
					else
						UDPServer.sin_addr.s_addr = DNS(HostName);
					UDPServer.sin_port = RemotePort;

					result = UDPSend(pS5UDP->Local.socks, RecvBuf + 10 + DataOffset, DataLength - DataOffset, &UDPServer, sizeof(UDPServer));
					if (result == SOCKET_ERROR)
					{
						//printf("sendto server error\n");
						break;
					}
					UDPClient.sin_port = SenderAddr.sin_port;
					//printf("Data(%d) sent to server succeed.|| Bytes: %d\n",DataLength-DataOffset,result);
				}
				else break;
			}
			else if (SenderAddr.sin_addr.s_addr == UDPServer.sin_addr.s_addr &&
				SenderAddr.sin_port == UDPServer.sin_port)  //Data come from server
			{
				// send data to client
				Socks5UDPHead *UDPHead = (Socks5UDPHead*)RecvBuf;
				memset(UDPHead, 0, 10);
				UDPHead->ATYP = 0x01;
				UDPHead->IP_PORT.IP = SenderAddr.sin_addr.s_addr;
				UDPHead->IP_PORT.Port = SenderAddr.sin_port;

				result = UDPSend(pS5UDP->Local.socks, RecvBuf, DataLength + 10, &UDPClient, sizeof(UDPClient));
				if (result == SOCKET_ERROR)
				{
					//printf("sendto client error\n");
					break;
				}
				//printf("Data(%d) sent to client succeed.|| Bytes: %d\n",DataLength+10,result);
			}
		}
		Sleep(5);
	}

	closesocket(pS5UDP->Local.socks);
	closesocket(pS5UDP->Client.socks);
}

void TCPTransfer(SOCKET* CSsocket){
	int result;
	SOCKET ClientSocket = CSsocket[0];
	SOCKET ServerSocket = CSsocket[1];
	struct timeval timeset;
	fd_set readfd, writefd;

	char SenderBuf[MAXBUFSIZE];
	char read_in1[MAXBUFSIZE], send_out1[MAXBUFSIZE];
	char read_in2[MAXBUFSIZE], send_out2[MAXBUFSIZE];

	int read1 = 0, totalread1 = 0, send1 = 0;
	int read2 = 0, totalread2 = 0, send2 = 0;
	int sendcount1, sendcount2;

	int maxfd = (int)(max(ClientSocket, ServerSocket)) + 1;
	int i = 0;

	memset(read_in1, 0, MAXBUFSIZE);
	memset(read_in2, 0, MAXBUFSIZE);
	memset(send_out1, 0, MAXBUFSIZE);
	memset(send_out2, 0, MAXBUFSIZE);

	timeset.tv_sec = TIMEOUT;
	timeset.tv_usec = 0;
	while (1)
	{
		FD_ZERO(&readfd);
		FD_ZERO(&writefd);

		FD_SET((UINT)ClientSocket, &readfd);
		FD_SET((UINT)ClientSocket, &writefd);
		FD_SET((UINT)ServerSocket, &writefd);
		FD_SET((UINT)ServerSocket, &readfd);

		result = select(maxfd, &readfd, &writefd, NULL, &timeset);
		if ((result<0) && (errno != EINTR))
		{
			printf("Select error.\r\n");
			break;
		}
		else if (result == 0)
		{
			printf("Socket time out.\r\n");
			break;
		}
		if (FD_ISSET(ServerSocket, &readfd))
		{
			if (totalread2<MAXBUFSIZE)
			{
				read2 = recv(ServerSocket, read_in2, MAXBUFSIZE - totalread2, 0);
				if (read2 == 0)break;
				if ((read2<0) && (errno != EINTR))
				{
					printf("Read ServerSocket data error,maybe close?\r\n\r\n");
					break;
				}

				memcpy(send_out2 + totalread2, read_in2, read2);

				totalread2 += read2;
				memset(read_in2, 0, MAXBUFSIZE);
			}
		}

		if (FD_ISSET(ClientSocket, &writefd)){
			int err2 = 0;
			sendcount2 = 0;
			while (totalread2>0){
				send2 = send(ClientSocket, send_out2 + sendcount2, totalread2, 0);
				if (send2 == 0)break;
				if ((send2<0) && (errno != EINTR))
				{
					printf("Send to ClientSocket unknow error.\r\n");
					err2 = 1;
					break;
				}
				if ((send2<0) && (errno == ENOSPC)) break;
				sendcount2 += send2;
				totalread2 -= send2;

			}//end while 

			if (err2 == 1) break;
			if ((totalread2>0) && (sendcount2 > 0))
			{
				/* move not sended data to start addr */
				memcpy(send_out2, send_out2 + sendcount2, totalread2);
				memset(send_out2 + totalread2, 0, MAXBUFSIZE - totalread2);
			}
			else
				memset(send_out2, 0, MAXBUFSIZE);
		}


		if (FD_ISSET(ClientSocket, &readfd))
		{
			if (totalread1<MAXBUFSIZE)
			{
				read1 = recv(ClientSocket, read_in1, MAXBUFSIZE - totalread1, 0);
				if ((read1 == SOCKET_ERROR) || (read1 == 0))
				{
					break;
				}

				memcpy(send_out1 + totalread1, read_in1, read1);

				totalread1 += read1;
				memset(read_in1, 0, MAXBUFSIZE);
			}
			if (SendRequest(CSsocket, SenderBuf, send_out1, totalread1))
				totalread1 = 0;
		}

		if (FD_ISSET(ServerSocket, &writefd))
		{
			int err = 0;
			sendcount1 = 0;
			while (totalread1>0)
			{
				send1 = send(ServerSocket, send_out1 + sendcount1, totalread1, 0);
				if (send1 == 0)break;
				if ((send1<0) && (errno != EINTR))
				{
					err = 1;
					break;
				}

				if ((send1<0) && (errno == ENOSPC)) break;
				sendcount1 += send1;
				totalread1 -= send1;

			}

			if (err == 1) break;
			if ((totalread1>0) && (sendcount1>0))
			{
				memcpy(send_out1, send_out1 + sendcount1, totalread1);
				memset(send_out1 + totalread1, 0, MAXBUFSIZE - totalread1);
			}
			else
				memset(send_out1, 0, MAXBUFSIZE);
		}
		Sleep(5);
	}

	closesocket(ClientSocket);
	closesocket(ServerSocket);
}

DWORD WINAPI ProxyThread(PVOID sClient){
	SOCKET CSsocket[2];
	CSsocket[0] = (SOCKET)sClient;
	CSsocket[1] = NULL;

	char buf[1024];
	memset(buf, 0, sizeof(buf));

	int DataLen = recv(CSsocket[0], buf, sizeof(buf), 0);
	if (DataLen < 3)
		goto exit;

	char ProxyType = buf[0];
	if (ProxyType == 5)
	{
		if (!DoSocks5(CSsocket, buf))
			goto exit;
	}
	else if (ProxyType == 4)
	{
		Socks4Req *Socks4Request = (Socks4Req *)buf;
		IP_PORT IPP;
		IPP.Port = Socks4Request->wPort;

		if (buf[4] != 0x00) //USERID !!
			IPP.IP = Socks4Request->dwIP;
		else
		{
			HOSTENT *hostent = gethostbyname((char*)&Socks4Request->other + 1);
			if (hostent == NULL)
				goto exit;

			IPP.IP = **(PULONG*)hostent->h_addr_list;
		}

		memset(Socks4Request, 0, 9);
		CSsocket[1] = ConnectToRemoteIP(&IPP);
		if (CSsocket[1])
			Socks4Request->REP = 0x5A; //GRANT  准许
		else
			Socks4Request->REP = 0x5B; //REJECT 拒绝

		if (send(CSsocket[0], (char *)Socks4Request, 8, 0) == SOCKET_ERROR)
			goto exit;

		if (Socks4Request->REP == 0x5B)
			goto exit;
	}
	else
	{
		if (!HttpProxy(CSsocket, buf, DataLen))
			goto exit;
	}

	if (CSsocket[0] && CSsocket[1])
		TCPTransfer(CSsocket);

exit:
	if (CSsocket[1])
		closesocket(CSsocket[1]);
	if (CSsocket[0])
		closesocket(CSsocket[0]);

	return 0;
}

void StartProxy(u_short LisPort){  // done!
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData))
		return;

	SOCKET sProxy = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sProxy == SOCKET_ERROR)
		return;

	struct sockaddr_in Server = { 0 };

	Server.sin_family = AF_INET;
	Server.sin_addr.S_un.S_addr = INADDR_ANY;
	Server.sin_port = htons(LisPort);

	if (bind(sProxy, (LPSOCKADDR)&Server, sizeof(Server)) == SOCKET_ERROR)
		return;

	if (listen(sProxy, SOMAXCONN) == SOCKET_ERROR)
		return;

	while (1){
		SOCKET sClient = accept(sProxy, NULL, NULL);
		HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ProxyThread, (PVOID)sClient, 0, NULL);
		if (hThread)
			CloseHandle(hThread);
	}

	closesocket(sProxy);
	WSACleanup();
}

void run(int argc, char* argv[]) {
	u_short LisPort = 1080;

	printf("SOCKS4 & SOCKS5 & Http Proxy V1.0\n""  usage:\n""    Socks5 [port] [UserName] [PassWord]\n\n");

	if (argc >= 2){
		LisPort = atoi(argv[1]);

		if (argc == 4){
			strcpy_s(g_Username, sizeof(g_Username), argv[2]);
			strcpy_s(g_Password, sizeof(g_Password), argv[3]);

			printf(" ~ Username : %s, Password : %s\n", g_Username, g_Password);
		}
	}
	strcpy_s(g_Username, sizeof(g_Username), "dd");
	strcpy_s(g_Password, sizeof(g_Password), "123");
	printf(" ~ ProxyPort = %d\n", LisPort);
	StartProxy(LisPort);
}


int main(int argc, char* argv[]){  // done!
	run(argc,argv);
	return 0;
}

你可能感兴趣的:(c++学习)