2008年1月25日星期五

[Tips]Linux平台socks5代理

by 云舒
2008-1-25
http://www.ph4nt0m.org


前几天MSN老上不去,我还以为是公司做了防火墙限制。于是把去年这个时候写得一个代理程序改了改,拿出来用。结果发现MSN是因为微软的问题,鄙视啊……
因为写得比较急,这个只支持TCP代理,UDP的我没写,因为MSN用不上。这个代码可以随意修改分发,不过最好能给我一份。

这是头文件:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Socks5代理头文件,定义协议相关数据包结构
// 版本 0.1,作者 云舒
// 2007年1月9日凌晨1点15分,GF回家已经11天了。
// 2008年1月25日修改,今年GF一直在我身边,哈哈
//
// 参考:
//              http://www.rfc-editor.org/rfc/rfc1928.txt
//              http://www.rfc-editor.org/rfc/rfc1929.txt
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef SOCKS5_H
#define SOCKS5_H

#define VERSION    0x05
#define CONNECT    0x01
#define IPV4        0x01
#define DOMAIN    0x03
#define IPV6        0x04

typedef 
struct _method_select_response    // 协商方法服务器响应
{
    
char    version;    // 服务器支持的Socks版本,0x04或者0x05
    char    select_method;// 服务器选择的方法,0x00为匿名,0x02为密码认证
} METHOD_SELECT_RESPONSE;

typedef 
struct _method_select_request    // 协商方法服务端请求
{
    
char      version;    // 客户端支持的版本,0x04或者0x05
    char    number_methods;    // 客户端支持的方法的数量
    char    methods[255]; // 客户端支持的方法类型,最多255个,0x00为匿名,0x02为密码认证
} METHOD_SELECT_REQUEST;

typedef 
struct _AUTH_RESPONSE // 用户密码认证服务端响应
{
    
char    version;// 版本,此处恒定为0x01
    char    result;// 服务端认证结果,0x00为成功,其他均为失败
} AUTH_RESPONSE;

typedef 
struct _AUTH_REQUEST //用户密码认证客户端请求
{
    
char    version; // 版本,此处恒定为0x01
    char        name_len;     // 第三个字段用户名的长度,一个字节,最长为0xff
    char        name[255]; // 用户名
    char        pwd_len;// 第四个字段密码的长度,一个字节,最长为0xff
    char        pwd[255];    // 密码
} AUTH_REQUEST;

typedef 
struct _SOCKS5_RESPONSE // 连接真实主机,Socks代理服务器响应
{
    
char        version; // 服务器支持的Socks版本,0x04或者0x05
    char        reply; // 代理服务器连接真实主机的结果,0x00成功
    char        reserved;    // 保留位,恒定位0x00
    char        address_type; // Socks代理服务器绑定的地址类型,IP V4为0x01,IP V6为0x04,域名为0x03                
    char        address_port[1];    // 如果address_type为域名,此处第一字节为域名长度,其后为域名本身,无0字符结尾,域名后为Socks代理服务器绑定端口
}SOCKS5_RESPONSE;                    

typedef 
struct _SOCKS5_REQUEST    // 客户端请求连接真实主机
{
    
char    version; // 客户端支持的Socks版本,0x04或者0x05
    char        cmd; // 客户端命令,CONNECT为0x01,BIND为0x02,UDP为0x03,一般为0x01
    char        reserved;    // 保留位,恒定位0x00
    char        address_type; // 客户端请求的真实主机的地址类型,IP V4为0x00,IP V6为0x04,域名为 0x03                                                char        address_port[1];    // 如果address_type为域名,此处第一字节为域名长度,其后为域名本身,无0字符结尾,域名后为真实主机绑定端口

}SOCKS5_REQUEST;                    
#endif 



主程序来了:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Socks5程序,只支持TCP代理
// 版本 0.1,作者 云舒
// 2007年1月9日凌晨1点15分,GF回家已经11天了。
// 2008年1月25日修改,今年GF一直在我身边,哈哈
//
// 参考:
//              http://www.rfc-editor.org/rfc/rfc1928.txt
//              http://www.rfc-editor.org/rfc/rfc1929.txt
//编译:
//        gcc -o socks5 -O2 Socks5.c -lpthread( RedHat AS5测试 )
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include 
<stdio.h>
#include 
<netinet/in.h>
#include 
<netdb.h>
#include 
<sys/time.h>
#include 
<sys/types.h>
#include 
<unistd.h>
#include 
<stdlib.h>
#include 
<signal.h>
#include 
<pthread.h>
#include 
<errno.h>
#include 
<string.h>
#include 
<sys/socket.h>
#include 
<arpa/inet.h>

#include 
"Socks5.h"

#define MAX_USER        10
#define BUFF_SIZE       1024

#define AUTH_CODE       0x02

#define TIME_OUT        6000000

#define USER_NAME       "yunshu"
#define PASS_WORD       "ph4nt0m"

// Select auth method, return 0 if success, -1 if failed
int     SelectMethod( int sock )
{
    
char    recv_buffer[BUFF_SIZE] = { 0 };
    
char    reply_buffer[2= { 0 };

    METHOD_SELECT_REQUEST    
*method_request;
    METHOD_SELECT_RESPONSE    
*method_response;
    
    
// recv METHOD_SELECT_REQUEST
    int ret = recv( sock, recv_buffer, BUFF_SIZE, 0 );
    
if( ret <= 0 )
    {
        perror( 
"recv error" );
        close( sock );

        
return -1;
    }

    
//printf( "SelectMethod: recv %d bytes\n", ret );

    
// if client request a wrong version or a wrong number_method
    method_request = (METHOD_SELECT_REQUEST *)recv_buffer;
    method_response 
= (METHOD_SELECT_RESPONSE *)reply_buffer;

    method_response
->version = VERSION;
    
    
// if not socks5
    if( (int)method_request->version != VERSION )
    {
        method_response
->select_method = 0xff;

        send( sock, method_response, 
sizeof(METHOD_SELECT_RESPONSE), 0 );
        close( sock );
    
        
return -1;
    }

    method_response
->select_method = AUTH_CODE;
    
if-1 == send( sock, method_response, sizeof(METHOD_SELECT_RESPONSE), 0 ) )
    {
        close( sock );
        
return -1;
    }
        
    
return 0;
}

// test password, return 0 for success.
int     AuthPassword( int sock )
{
    
char    recv_buffer[BUFF_SIZE] = { 0 };
    
char    reply_buffer[BUFF_SIZE] = { 0 };

    AUTH_REQUEST    
*auth_request;
    AUTH_RESPONSE    
*auth_response;
    
    
// auth username and password
    int ret = recv( sock, recv_buffer, BUFF_SIZE, 0 );
    
if( ret <= 0 )
    {
        perror( 
"recv username and password error" );
        close( sock );
        
return -1;
    }
    
//printf( "AuthPass: recv %d bytes\n", ret );

    auth_request 
= (AUTH_REQUEST *)recv_buffer;

    memset( reply_buffer, 
0, BUFF_SIZE );
    auth_response 
= (AUTH_RESPONSE *)reply_buffer;
    auth_response
->version = 0x01;

    
char    recv_name[256= { 0 };
    
char    recv_pass[256= { 0 };

    
// auth_request->name_len is a char, max number is 0xff
    char pwd_str[2= { 0 };
    strncpy( pwd_str, auth_request
->name + auth_request->name_len, 1 );
    
int pwd_len = (int)pwd_str[0];

    strncpy( recv_name, auth_request
->name, auth_request->name_len );
    strncpy( recv_pass, auth_request
->name + auth_request->name_len + sizeof(auth_request->pwd_len), pwd_len );

    
//printf( "username: %s\npassword: %s\n", recv_name, recv_pass );
    
// check username and password
    if( (strncmp( recv_name, USER_NAME, strlen(USER_NAME) ) == 0&&
            (strncmp( recv_pass, PASS_WORD, strlen(PASS_WORD) ) 
== 0)
        )
    {
        auth_response
->result = 0x00;
        
if-1 == send( sock, auth_response, sizeof(AUTH_RESPONSE), 0 ) )
        {
            close( sock );
            
return -1;
        }
        
else
        {
            
return 0;
        }
    }
    
else
    {
        auth_response
->result = 0x01;
        send( sock, auth_response, 
sizeof(AUTH_RESPONSE), 0 );

        close( sock );
        
return -1;
    }
}

// parse command, and try to connect real server.
// return socket for success, -1 for failed.
int    ParseCommand( int sock )
{
    
char    recv_buffer[BUFF_SIZE] = { 0 };
    
char    reply_buffer[BUFF_SIZE] = { 0 };
    
    SOCKS5_REQUEST    
*socks5_request;
    SOCKS5_RESPONSE    
*socks5_response;

    
// recv command
    int ret = recv( sock, recv_buffer, BUFF_SIZE, 0 );
    
if( ret <= 0 )
    {
        perror( 
"recv connect command error" );

        close( sock );
        
return -1;
    }
    
    socks5_request 
= (SOCKS5_REQUEST *)recv_buffer;
    
if( (socks5_request->version != VERSION) || (socks5_request->cmd != CONNECT) ||
        (socks5_request
->address_type == IPV6) )
    {
        
//printf( "connect command error.\n" );
        close( sock );
        
return -1;
    }

    
// begain process connect request
    struct sockaddr_in    sin;

    memset( (
void *)&sin, 0sizeof(struct sockaddr_in) );
    sin.sin_family 
= AF_INET;

    
// get real server's ip address
    if( socks5_request->address_type == IPV4 )
    {
        memcpy( 
&sin.sin_addr.s_addr, &socks5_request->address_type + sizeof(socks5_request->address_type) , 4 );
        memcpy( 
&sin.sin_port, &socks5_request->address_type + sizeof(socks5_request->address_type) + 42 );

        
//printf( "Real Server: %s %d\n", inet_ntoa( sin.sin_addr ), ntohs( sin.sin_port ) );
    }
    
else if( socks5_request->address_type == DOMAIN )
    {
        
char domain_length = *(&socks5_request->address_type + sizeof(socks5_request->address_type));
        
char target_domain[ 256= { 0 };

        strncpy( target_domain, 
&socks5_request->address_type + 2, (unsigned int)domain_length );

        
//printf( "target: %s\n", target_domain );

        
struct hostent *phost = gethostbyname( target_domain );
        
if( phost == NULL )
        {
            
//printf( "Resolve %s error!\n" , target_domain );

            close( sock );
            
return -1;
        }
        memcpy( 
&sin.sin_addr , phost->h_addr_list[0] , phost->h_length );

        memcpy( 
&sin.sin_port, &socks5_request->address_type + sizeof(socks5_request->address_type) +
                
sizeof(domain_length) + domain_length, 2 );
    }

    
// try to connect to real server
    int     real_server_sock = socket( AF_INET, SOCK_STREAM, 0 );
    
if( real_server_sock < 0 )
    {
        perror( 
"Socket creation failed\n");
            
        close( sock );
        
return -1;
    }

    memset( reply_buffer, 
0sizeof(BUFF_SIZE) );

    socks5_response 
= (SOCKS5_RESPONSE *)reply_buffer;

    socks5_response
->version = VERSION;
    socks5_response
->reserved = 0x00;
    socks5_response
->address_type = 0x01;
    memset( socks5_response 
+ 40 , 6 );

    ret 
= connect( real_server_sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in) );
    
if( ret == 0 )
    {
        socks5_response
->reply = 0x00;
        
if-1 == send( sock, socks5_response, 100 ) )
        {
            close( sock );
            
            
return -1;
        }
    }
    
else
    {
        perror( 
"Connect to real server error" );
        socks5_response
->reply = 0x01;
        send( sock, socks5_response, 
100 );
        
        close( sock );
        
return -1;
    }
    
    
return real_server_sock;
}

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;
    
    
while1 )
    {
        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 )
        {
            perror( 
"select socket error" );
            
break;
        }
        
else if0 == ret )
        {
            
//perror( "select time out" );
            continue;
        }
        
        
//printf( "[DEBUG] testing readable!\n" );
        if( FD_ISSET(sock, &fd_read) )
        {
            
//printf( "client can read!\n" );
            memset( recv_buffer, 0, BUFF_SIZE );
            ret 
= recv( sock, recv_buffer, BUFF_SIZE, 0 );
            
if( ret > 0 )
            {
                
//printf( "%s", recv_buffer );
                
//printf( "recv %d bytes from client.\n", ret );
                ret = send( real_server_sock, recv_buffer, ret, 0 );
                
if( ret == -1 )
                {
                    perror( 
"send data to real server error" );
                    
break;
                }
                
//printf( "send %d bytes to client!\n", ret );
            }
            
else if( ret == 0 )
            {
                
//printf( "client close socket.\n" );
                break;
            }
            
else
            {
                
//perror( "recv from client error" );
                break;
            }
        }
        
        
else if( FD_ISSET(real_server_sock, &fd_read) )
        {
            
//printf( "real server can read!\n" );
            memset( recv_buffer, 0, BUFF_SIZE );
            ret 
= recv( real_server_sock, recv_buffer, BUFF_SIZE, 0 );
            
if( ret > 0 )
            {
                
//printf( "%s", recv_buffer );
                
//printf( "recv %d bytes from real server.\n", ret );
                ret = send( sock, recv_buffer, ret, 0 );
                
if( ret == -1 )
                {
                    perror( 
"send data to client error" );
                    
break;
                }
            }
            
else if( ret == 0 )
            {
                
//printf( "real server close socket.\n" );
                break;
            }
            
else
            {
                perror( 
"recv from real server error" );
                
break;
            }
        }
    }
    
    
return 0;
}

int    Socks5( void *client_sock )
{
    
int     sock = *(int *)client_sock;

    
if( SelectMethod( sock ) == -1 )
    {
        
//printf( "socks version error\n" );
        return -1;
    }
    
    
if( AuthPassword( sock ) == -1 )
    {
        
//printf( "auth password error\n" );
        return -1;
    }
    
    
int real_server_sock = ParseCommand( sock );
    
if( real_server_sock == -1 )
    {
        
//printf( "parse command error.\n" );
        return -1;
    }
    
    ForwardData( sock, real_server_sock );

    close( sock );
    close( real_server_sock );
    
    
return 0;
}

int     main( int argc, char *argv[] )
{
    
if( argc != 2 )
    {
        printf( 
"Socks5 proxy for test,code by YunShu\n" );
        printf( 
"Usage: %s   <proxy_port>\n", argv[0] );
        printf( 
"Options:\n" );
        printf( 
"        <proxy_port>     ---which  port of this proxy server will listen.\n" );
                
        
return 1;
    }

    
struct sockaddr_in      sin;

    memset( (
void *)&sin, 0sizeofstruct sockaddr_in) );
    sin.sin_family 
= AF_INET;
    sin.sin_port 
= htons( atoi(argv[1]) );
    sin.sin_addr.s_addr 
= htonl(INADDR_ANY);

    
int     listen_sock = socket( AF_INET, SOCK_STREAM, 0 );
    
if( listen_sock < 0 )
    {
        perror( 
"Socket creation failed\n");
        
return -1;
    }

    
int opt = SO_REUSEADDR;
    setsockopt( listen_sock, SOL_SOCKET, SO_REUSEADDR, 
&opt, sizeof(opt) );

    
if( bind( listen_sock, (struct sockaddr*)&sin, sizeof(struct sockaddr_in) ) < 0 )
    {
        perror( 
"Bind error" );
        
return -1;
    }

    
if( listen( listen_sock, MAX_USER ) < 0 )
    {
        perror( 
"Listen error" );
        
return -1;
    }

    
struct  sockaddr_in     cin;
    
int       client_sock;
    
int       client_len = sizeofstruct sockaddr_in );

    
while( client_sock = accept( listen_sock, (struct sockaddr *)&cin, (socklen_t *)&client_len ) )
    {
        printf( 
"Connected from %s, processing\n", inet_ntoa( cin.sin_addr ) );

        pthread_t       work_thread;
        
if( pthread_create( &work_thread, NULL, (void *)Socks5, (void *)&client_sock ) )
        {
            perror( 
"Create thread error" );
            close( client_sock );
        }
        
else
        {
            pthread_detach( work_thread );
        }
    }

没有评论: