概述
写一个简易的socks5代理服务器,负责转发网络数据包,要能够使用它来上网。
SOCKS5 是一个代理协议,它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过TCP/IP协议进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。
socks5协议认证步骤:
1、客户端连接到服务器,向服务器发送一个版本标识/方法选择报文:
+-------+------------------+----------------+
| VER | NMETHODS | METHODS |
+-------+------------------+----------------+
| 1 | 1 | 1~255 |
+-------+------------------+----------------+
VER:客户端的协议版本号(socks5:0x05 socks4:0x04)
NMETHODS:客户端所支持认证方式的长度
METHODS:客户端支持的认证方式
目前定义的METHOD有以下几种:
0x00 无需认证
0x01 通用安全服务应用程序
0x02 用户名/密码
0x03- 0x7F IANA 分配
0x80- 0xFE 私人方法保留
0xFF 无可接受方法
2、服务器从客户端报文中选择一种方式,并发送一个方式选择报文:
+----+----------+
|VER | METHOD |
+----+----------+
| 1 | 1 |
+----+----------+
VER:服务端的协议版本号(socks5:0x05 socks4:0x04)
METHOD:服务端选择的认证方式
3、客户端向服务器发送连接目的服务器的请求报文:
+----+-----+-------+------+------------+----------+
|VER | CMD| RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+------------+----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+------------+----------+
VER:客户端协议版本号(socks5:0x05 socks4:0x04)
CMD:请求类型(CONNECT:0x00 BIND:0x01 UDP:0x02)
RSV:保留位(要求为0x00)
ATYP:IP类型(IPV4:0x01 IPV6:0X04 域名:0x03)
DST.ADDR:目的服务器地址
DST.ADDR:目的服务器端口
4、服务器向客户端回应连接请求
+----+-----+-------+------+-------------+-----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+-------------+-----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+----------+--------------+
VER:服务端协议版本号(socks5:0x05 socks4:0x04)
REP:应答结果
1. 0x00 成功
2. 0x01 一般性失败
3. 0x02 规则不允许转发
4. 0x03 网络不可达
5. 0x04 主机不可达
6. 0x05 连接拒绝
7. 0x06 TTL超时
8. 0x07 不支持请求包中的CMD
9. 0x08 不支持请求包中的ATYP
10. 0x09-0xFF 未定义
RSV:保留位(要求为0x00)
ATYP:指明后面的地址类型
BND.ADDR:客户端请求报文中CMD的回应地址(CMD为0x01时)
BND.PORT:客户端请求报文中CMD的回应端口(CMD为0x01时)
程序主要步骤:
第一步:建立客户端和代理服务器连接,TCP连接
第二步:socks5协议认证
第三步:代理服务器连接目的服务器
第四步:转发数据
代码
#include <unistd.h>
#include <arpa/inet.h> //因特网地址结构体相关
#include <sys/socket.h> //socket相关
#include <time.h> //时间相关
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/epoll.h>
#define BUFF_SIZE 1024 //设置转发缓冲区
#define TIME_OUT 6000000 //设置复用IO延时
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
//++++++++++++ sock5协议结构体定义 ++++++++++++++
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*
一、客户端认证请求
+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1~255 |
+----+----------+----------+
二、服务端回应认证
+----+--------+
|VER | METHOD |
+----+--------+
| 1 | 1 |
+----+--------+
三、客户端连接请求(连接目的网络)
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
四、服务端回应连接
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
*/
//以下为协议结构体定义
//一、客户端认证请求
typedef struct client_license_request{
char ver; // 客户端的协议版本号 0x05:socks5 0x04:socks4
char nmethods; // 客户端所支持认证方式的长度
char methods[255]; //客户端支持的认证方式(可以有255种)
}client_license_request;
//二、服务端回应认证
typedef struct server_license_response{
char ver; // 服务端的协议版本号
char method; //服务端选择的认证方式
}server_license_response;
//三、客户端连接请求
typedef struct client_connect_request{
char ver; //客户端协议版本号
char cmd; //连接方式
char rsv; //保留位0x00
char type; //类型
char addr[4]; //目的服务器ip
char port[2]; //目的服务器端口
}client_connect_request;
//四、服务端回应连接
typedef struct server_connect_response{
char ver; //版本
char rep; //连接状态
char rsv; //保留0x00
char type; //类型
char addr[4]; //bind ip
char port[2]; //bind port
}server_connect_response;
int socketfd_tcp; //TCP监听套接字
//转发
int ForwardData( int sock, int real_server_sock )
{
char recv_buffer[BUFF_SIZE] = { 0 };
fd_set fd_read;
struct timeval time_out;
time_out.tv_sec = 0;
time_out.tv_usec = TIME_OUT;
int ret = 0;
printf("线程%u-开始进行数据转发n",(int)pthread_self());
while( 1 )
{
FD_ZERO( &fd_read );
FD_SET( sock, &fd_read );
FD_SET( real_server_sock, &fd_read );
ret = select( (sock>real_server_sock?sock:real_server_sock) + 1,&fd_read, NULL, NULL, &time_out);
if( -1 == ret )
{
break;
}
else if( 0 == ret )
{
continue;
}
if( FD_ISSET(sock, &fd_read) )
{
memset( recv_buffer, 0, BUFF_SIZE );
ret = recv( sock, recv_buffer, BUFF_SIZE, 0 );
if( ret >0 )
{
ret = send( real_server_sock, recv_buffer, ret, 0 );
if( ret == -1 )
{
break;
}
}
else if( ret == 0 )
{
break;
}
else
{
break;
}
}
else if( FD_ISSET(real_server_sock, &fd_read) )
{
memset(recv_buffer,0,BUFF_SIZE );
ret = recv( real_server_sock, recv_buffer, BUFF_SIZE, 0 );
if( ret > 0 )
{
ret = send( sock, recv_buffer, ret, 0 );
if( ret == -1 )
{
break;
}
}
else if(ret==0)
{
break;
}
else
{
break;
}
}
}
return 0;
}
//创建TCP套接字
void tcp_creat()
{
socketfd_tcp=socket(AF_INET,SOCK_STREAM,0);
if(socketfd_tcp==-1)
{
perror("socketfd_tcp");
exit(-1);
}
struct sockaddr_in addr_tcp;
bzero(&addr_tcp,sizeof(addr_tcp));
addr_tcp.sin_family=AF_INET;
addr_tcp.sin_port=htons(2018);
addr_tcp.sin_addr.s_addr=INADDR_ANY;
int re=bind(socketfd_tcp,(struct sockaddr *)&addr_tcp,sizeof(addr_tcp));
if(re==-1)
{
perror("bind");
exit(-1);
}
re=listen(socketfd_tcp,100); //队列长度设为100
if(re==-1)
{
perror("listen");
exit(-1);
}
}
//代理服务器连接目的服务器
int connect_dest_server(client_connect_request * connect_request)
{
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1)
{
perror("socketfd_tcp");
return -1;
}
struct sockaddr_in sin_server;
bzero(&sin_server,sizeof(sin_server));
sin_server.sin_family=AF_INET;
//目的服务器ip填入结构体
memcpy(&sin_server.sin_addr,&connect_request->addr,sizeof(connect_request->addr));
//目的服务器的端口号填入结构体
memcpy(&sin_server.sin_port,&connect_request->port,sizeof(connect_request->port));
/*2 连接服务器*/
int re=connect(fd,(struct sockaddr *)&sin_server,sizeof(sin_server));
if(re==-1)
{
// printf("目的服务器连接失败n");
return -1;
}
// printf("目的服务器连接成功n");
return fd;
}
//socks5认证连接
int sock5_license(struct sockaddr_in *addr_client,socklen_t len,int fd)
{
//接收认证信息
char buffer[30]={0};
read(fd,buffer,sizeof(buffer));
client_license_request * license_request=(client_license_request *)buffer;
//验证认证信息
// printf("客户端版本%dn",license_request->ver);
if(license_request->ver!=0x5)
{
// printf("协议版本错误n");
return 0;
}
// printf("客户认证信息通过,回应认证请求n");
server_license_response license_response;
license_response.ver=0x5;
license_response.method=0x0;
char buff[2]={0};
memcpy(buff,&license_response,sizeof(buff));
//回应认证信息
write(fd,buff,sizeof(buff));
// printf("已发送回应请求n");
//接收连接请求
bzero(&buffer,sizeof(buffer));
// printf("等待接收客户连接请求n");
read(fd,buffer,sizeof(buffer));
client_connect_request * connect_request=(client_connect_request *)buffer;
//认证连接请求
if(connect_request->ver!=0x5)
{
// printf("连接请求协议版本错误n");
return 0;
}
if(connect_request->cmd!=0x1)
{
// printf("连接请求命令错误(非TCP)n");
return 0;
}
if(connect_request->type!=0x01)
{
// printf("连接请求类型错误(非IPV4)n");
return 0;
}
//连接客户端指定的目的地址
int dest_fd=connect_dest_server(connect_request);
if(dest_fd==-1)
{
return -1;
}
//成功连接则发送回应信息
//回应连接信息
char buffer1[10]={0};
bzero(&buffer,sizeof(buffer1));
server_connect_response connect_response;
bzero(&connect_response,sizeof(connect_response));
connect_response.ver=0x5;
connect_response.rep=0x00; //连接成功标志
connect_response.rsv=0x00;
connect_response.type=0x01;
memcpy(buffer1,&connect_response,sizeof(connect_response));//服务端回应数据 设置版本号与结果位,ip与端口号未使用
write(fd,buffer1,sizeof(buffer1));
// printf("已发送回应请求n");
//全部认证连接建立完成
//执行转发程序
ForwardData(fd,dest_fd);
}
//等待TCP连接,每个客户端分出一条线程,跳转执行socks5认证函数↑↑↑
void * pthread_tcp(void * arg)
{
printf("线程%u-正在运行n",(int)pthread_self());
struct sockaddr_in addr_client;
socklen_t len=sizeof(addr_client);
bzero(&addr_client,sizeof(addr_client));
int fd=accept(socketfd_tcp,(struct sockaddr *)&addr_client,&len);
pthread_t pid2;
pthread_create(&pid2,NULL,pthread_tcp,NULL);
//打印客户端信息
char ip[20]={0};
unsigned short port;
inet_ntop(AF_INET,&addr_client.sin_addr,ip,len);
port=ntohs(addr_client.sin_port); //转换为本机字节序
printf("%s:%hu已连接n",ip,port);
//执行socks5认证
sock5_license(&addr_client,len,fd);
printf("线程%u-退出n",(int)pthread_self());
return NULL;
}
int main(void)
{
//创建TCP套接字
tcp_creat();
printf("初始化完成等待连接n");
while(1)
{
printf("主线程%u-正在运行n",(int)pthread_self());
pthread_tcp(NULL);
}
}
最后
以上就是无语曲奇为你收集整理的使用socks5实现简易代理服务器的全部内容,希望文章能够帮你解决使用socks5实现简易代理服务器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复