SOCKS5中的UDP穿透

socket5是一种代理协议,如果防火墙禁止所有的计算机发送udp包,代理也没用。 
http隧道技术 是在防火墙禁止了udp的数据包,可以把数据封装在http包中,防火墙让看起来像http包的数据包通过,在防火墙外有专门的计算机把http包还原成udp包。



1. SOCKS5

1.1  简介

SOCKS5大家肯定很熟悉,是我们常用的代理协议的一种。它是Socks协议的第五版,相对于第四版增加了对身份验证,UDP,IPV6的支持。

一般的代理协议(比如Http代理)都是工作在应用层的,功能单一。而Socks代理协议旨在提供一种通用的代理服务,所以工作在应用层和传输层之间,只是传递传输层网络数据包(TCP/UDP),对于应用层的协议并不关心。

1.2 SOCKS5中的方法

SOCKS5在使用时是典型的CS模式的。SOCKS5服务可以分成两大功能,身份认证和代理服务。每个功能又支持不定的多种方法(Method)。

身份认证常用的方法是不需认证(No Authentication Required)和用户名/密码认证(Username/Password),多数应用软件包括我们监控CU都只支持这两种方法。

代理服务的模式有连接(Connect),绑定(Bind)和UDP穿透(UDP Associate)。OSP通过代理连接服务器的时候,是用的连接模式。通过代理服务器看码流的时候用的是UDP穿透模式。本案例只介绍一下UDP穿透模式的过程。

1.3 信令

SOCKS5的信令都是按位解释的。一般的信令表示如下:

          +----+----------+----------+

          |VER | NMETHODS | METHODS  |

          +----+----------+----------+

          | 1  |    1     | 1 to 255 |

           +----+----------+----------+

第一行是信令中该位置表示的含义。第二行是长度。

2 UDP穿透

UDP穿透(UDP Associate)是SOCKS5新加入的功能。

2.1 流程

    

图表 1 UDP 穿透过程

流程如上图。下面具体讲各个交互信令。

2.2 连接和认证

客户端TCP连接SOCKS5代理服务器的服务端口,并完成认证过程。不详述。

2.3 UDP穿透请求

客户端会用通过认证的这个TCP连接发送UDP穿透请求,信令格式如下:

    +----+-----+-------+------+----------+----------+

    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |

    +----+-----+-------+------+----------+----------+

    | 1  |  1  | X'00' |  1   | Variable |    2     |

    +----+-----+-------+------+----------+----------+

其中各项:

o  VER    协议版本,对于SOCKS5都是 5。

      o  CMD      要请求的命令,UDP穿透填3。 其它的CONNECT是1,BIND是2。

      o  RSV    保留字段,填0。

      o  ATYP   地址类型,我们用IPV4,填1。域名的话填3,IPV6的话填4。

      o  DST.ADDR   IP地址。对于UDP穿透来说,好像没有什么意义,填0(也就是127.0.0.1)。

      o  DST.PORT  这个很重要,要填客户端想发送/接收UDP包的本地端口。后面在发送UDP包时代理服务器会检测收到的UDP包的源端口,只有和这里填入的端口号符合的包才会被处理。(CCProxy收到源端口错误的包会出bug,狂发包。)

Demo中的对应代码:

    char abyUdpAssociateBuf[1024] = { 0 };
 
    //sock5代理版本号,当然是5了。
    const int SOCK5_PROXY_VERSION = 0x05;
    const int CMD_UDP_ASSOCIATE = 0x03;
    const int RESERVED = 0;
    const int IP_TYPE = 0x01;   // ipv4
 
    int nAddr = inet_addr( m_strLocalIp );
    short nPort = htons( (short)m_nPortReq );
 
    abyUdpAssociateBuf[0] = SOCK5_PROXY_VERSION;
    abyUdpAssociateBuf[1] = CMD_UDP_ASSOCIATE;
    abyUdpAssociateBuf[2] = RESERVED;
    abyUdpAssociateBuf[3] = IP_TYPE;
    memcpy( &abyUdpAssociateBuf[4], &nAddr, 4 );
    memcpy( &abyUdpAssociateBuf[8], &nPort, 2 );
 
    if( SOCKET_ERROR == send( this->m_hProxyControl, abyUdpAssociateBuf, 10 , 0 ) )
    {
       this->ShowAssociateMsg( "Send associate msg fail!" );
       this->Release();
       return ;
    }

2.4 UDP穿透应答

代理服务器会回应客户端的请求,消息格式如下:

+----+-----+-------+------+----------+----------+

    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |

    +----+-----+-------+------+----------+----------+

    | 1  |  1  | X'00' |  1   | Variable |    2     |

    +----+-----+-------+------+----------+----------+

其中:

o  VER    协议版本,对于SOCKS5都是 5。

      o  REP    代理服务器返回的结果,含义如下:

      X'00' succeeded

      X'01' general SOCKS server failure

      X'02' connection not allowed by ruleset

      X'03' Network unreachable

      X'04' Host unreachable

      X'05' Connection refused

      X'06' TTL expired

      X'07' Command not supported

      X'08' Address type not supported

      X'09' to X'FF' unassigned

      o  RSV    保留字段。

      o  ATYP   后面IP地址的格式,含义如下:

      IP V4 address: X'01'

DOMAINNAME: X'03'

      IP V6 address: X'04'

      o  BND.ADDR   此UDP穿透通道对应的代理服务器地址。

      o  BND.PORT    此UDP穿透通道对应的代理服务器端口。

至此,UDP穿透通道已经被建起来了,客户端只要按标准格式将UDP包发往上述地址端口,UDP包就会被代理服务器转发出去。

Demo中对应的代码:

 

   if( 10 != recv( this->m_hProxyControl, abyUdpAssociateBuf, sizeof(abyUdpAssociateBuf), 0 ) )
    {
       this->ShowAssociateMsg( "Receive reply of UDP Associate req fail!" );
       return;
    }
 
    // 校验返回值。
    const int SOCK5_PROXY_SUCCESS = 0;
    if ( SOCK5_PROXY_VERSION != abyUdpAssociateBuf[0]
       || SOCK5_PROXY_SUCCESS != abyUdpAssociateBuf[1]
       || IP_TYPE != abyUdpAssociateBuf[3] )
    {
       this->ShowAssociateMsg( "proxy error!" );
       return;
    }
 
    // 看服务器返回的地址。
    m_strIPProxyReply = inet_ntoa( *(IN_ADDR*)(&abyUdpAssociateBuf[4]) );
    m_sPortProxyReply = ntohs( *(short*)( &abyUdpAssociateBuf[8] ) );
 
    this->ShowAssociateMsg( "UDP Associate success!" );

2.5 UDP包发出

上面交互信令中都没有提到客户端想通过代理访问的远端服务器地址。UDP包的最终目的地是通过在原始UDP包数据前加一个包头来实现的。包头的格式如下:

    +----+------+------+----------+----------+----------+

    |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |

    +----+------+------+----------+----------+----------+

    | 2  |  1   |  1   | Variable |    2     | Variable |

    +----+------+------+----------+----------+----------+

其中:

o  RSV  保留字段,填0。

       o  FRAG    当前分片序号,我们没有分片,填0。

       o  ATYP    地址类型,和前面的几个一样。IPV4填 1 。

       o  DST.ADDR  UDP包最终的目的地址。

       o  DST.PORT   UDP包最终的目的端口。

       o  DATA     原始的UDP包的数据。

按照上面格式发出的UDP包中的DATA部分会被代理服务器转发到包头中填入的最终目的地址。也就是说,我们用一路UDP穿透通道可以向不同的服务器发送数据。因为包头会被代理服务器去掉,所以远端服务器是不用知道客户端是否使用了代理。

Demo中对应的包头组装代码:

    const int BUF_SIZE = 1024;
    char abySentBuf[ BUF_SIZE ];
    char *pCursor = abySentBuf;
 
    *(short*)pCursor = 0;    // RSV  Reserved X'0000'
    pCursor += 2;
   
    *pCursor = 0; // Current fragment number
    pCursor++;
 
    *pCursor = 0x01;  // IP V4 address: X'01'
    pCursor ++;
 
    int nIp = inet_addr( m_strRemoteIp );
    *(int*)pCursor = nIp;    // desired destination address
    pCursor += 4;
 
    *(short*)pCursor = htons( m_nRemotePort );
    pCursor += 2;
 
    // 最后是我们的消息。
    strcpy( pCursor, this->m_strMsgSentToRemote );
    pCursor += this->m_strMsgSentToRemote.GetLength() + 1;
 
    int nDataLen = pCursor - abySentBuf;

2.6 包发入

远端服务器不用知道客户端是否使用了代理,它只要将需要回复的UDP包发送到它接收UDP包的源端口就行,在远端服务器看来表现和NAT表现一样。

但代理服务器不会直接转发发入的包,它会在原始数据包前面再封装一个包头然后发送给客户端。这个包头和上面的格式是一样的。客户端在处理之前,首先要去掉或跳过这个包头,才能得到服务器发送过来的原始数据。

3 实战分析

白话半天容易晕,直接上图,不解释。

图表 2 监控系统中的码流代理UDP穿透示意图

 

4         容易出现的问题

4.1 链路的断链情况

在标准中说的很清楚,UDP通道和发送请求的TCP连接是拴在一根绳上的蚂蚱。如果这个TCP连接断开了,UDP通道也会跟着生效。反过来,如果因为某些原因导致UDP通道失效了(比如客户端发送了一个错误的UDP包,导致代理服务器停掉UDP通道),那么这个TCP连接也会被跟着中断掉。所以客户端程序必须要检测这个TCP连接的情况,如果发现断链了,就需要重新打洞。

4.2 每个TCP连接只能请求一路UDP通道,虽然标准中并没有此限制,但CCProxy就是这么做的。请求第二路通道会返回失败,此前打通的那路也会断掉。这样,因为监控客户端需要的码流通道非常多,现在每个客户端登录后要开157路UDP通道,对应157个TCP连接,对代理服务器的压力很大,估计支撑不了几个人同时登录。还有就是代理转发效率可能不高,像CCProxy在高码率下会严重丢包。

这个暂时还没有什么好办法解决。

5 附件

5.1 Demo 程序

在这里下载源码:

 http://download.csdn.net/source/2508913

5.2        Rfc文档

当然,标准文档直接找也可以。

http://download.csdn.net/source/2508935



  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Php+Mysql电脑网页版通用考试成绩查分系统:Linux(Utf-8) 你可以轻松应用于各种成绩查询、工资查询、物业费查询、水电费查询、分班查询、录取查询、考场查询、证书查询等修改不频繁保密性不高的各种查询系统。 201911更新内容: 1.完全重写了使用说明书及说明书风格。 2. 查询页面说明书单独文件inc/desc.txt修改。 3. 查询功能安全性加强,从而支持更多符号。 使用用途: 适合修改不频繁、保密性不高的成绩、工资、物业水电费等各种精准查询。 1. 成绩查询系统,每个学校,教育机构,事业单位考试等都可以用到 2. 工资查询系统,每个学校,教育机构,事业单位考试等都可以用到 3. 物业费查询系统,每个企业,学校,所有单位都可能用到 4. 水电费查询系统,小区,物业公司,大学寝室等 5. 其他如分班查询,录取查询,考场查询、证书查询等修改不多的各种查询系统 特色优势 1. 灵活通用 别看源码体积极小,它无需设计数据库,可以快速用于各种二维表查询。 成绩查询、水电费物业费查询、录取、分班、分宿舍、考场分配查询等通用。 2. 小巧易拓展 整个源码(不含演示数据)只有17KB,不仅运行起来自然流畅。 易于程序员快速掌握;大大提升开发速度并降低二次开发成本。 易于初学者快速入门PHP;大大降低开发速度并降低二次开发成本。 3. 低耗网络 现在大多页面单js文件就几百KB,加上图片就更大了。 一个网页往往超过1MB。本查询页一个页面只有30KB左右。 相当于在同等带宽资源下,网络并发量可以提升几十倍。 所以:节省很多网络费用的同时,大幅度提升查询的并发量。 4. 低耗读写 本查询几乎只读不写;小巧的网页也将在JS和图片的读取方面节约大量资源。 使用须知 适合已有现成数据库快速调用查询或者熟悉mysql数据导入的用户使用。 查询功能很通用,注意不要用于法律法规不允许的用途。 不要冒用他人名义发布信息,发布查询请注意隐私信息保护。 使用建议: 然后notepad++打开inc/sqls.Php修改mysql参数。 然后notepad++打开inc/conn.Php查看参数与网页对应关系。 直接通过FTP上传源码至服务器空间。 前台访问:http://网址/目录/ (请先配置mysql数据及导入数据) Mysql版数据打开方式:phpmyadmin以及服务器环境比如宝塔自带软件。 环境建议:php5.3+,mysql 5.6+。 局限性说明 1. 修改越频繁越不适合(成绩、工资、水电费等一般都一次性出来不修改) 2. 只适合二维表(一般数据库都采用二维结构,首行列标题,以后一行一条数据) 3. 建议单库控制在100万条以内(注意使用索引优化)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值