使用socks5实现简易代理服务器

写一个简易的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("客户端版本%d\n",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);
    }

}

 

 

 

 

 

 

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lmory233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值