Linux平台socks5代理

导读:
  前几天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
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #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, 0, sizeof(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) + 4, 2 );
  //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, 0, sizeof(BUFF_SIZE) );
  socks5_response = (SOCKS5_RESPONSE *)reply_buffer;
  socks5_response->version = VERSION;
  socks5_response->reserved = 0x00;
  socks5_response->address_type = 0x01;
  memset( socks5_response + 4, 0 , 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, 10, 0 ) )
  {
  close( sock );
  
  return -1;
  }
  }
  else
  {
  perror( "Connect to real server error" );
  socks5_response->reply = 0x01;
  send( sock, socks5_response, 10, 0 );
  
  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;
  
  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 )
  {
  perror( "select socket error" );
  break;
  }
  else if( 0 == 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 /n", argv[0] );
  printf( "Options:/n" );
  printf( " ---which port of this proxy server will listen./n" );
  
  return 1;
  }
  struct sockaddr_in sin;
  memset( (void *)&sin, 0, sizeof( struct 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 = sizeof( struct 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 );
  }
  }
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值