• 用几种语言实现socks server


    用几种语言实现socks server

    总结

    • socks协议本身比较简单,编写难度不大
    • 首先要会用工具解析封包,这样一是了解协议,二是方便调试代码的问题
    • c++的主要难点在与
      • boost,cmake工具链
      • 要留意变量生命周期,需要的时候用share_ptr
    • rust
      • ??
    • python
      • ??

    rust

    参考实现 https://github.com/WANG-lp/socks5-rs

    c++

    同步

    使用boost asio

    代码包含两部分,协议分析层,和网络层

    • 协议分析层
      • verify1: 握手
      • Cmd:链接目标
    • 网络层
      • read
      • write
      • connect
      • thread:数据转发
    #include <ctime>
    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    #include <boost/array.hpp>
    #include <boost/thread.hpp>
    #include <vector>
    #include <assert.h>
    
    using boost::asio::ip::tcp;
    
    std::string make_daytime_string()
    {
        using namespace std; // For time_t, time and ctime;
        time_t now = time(0);
        return ctime(&now);
    }
    
    class verify1
    {
        int ver;
        
        enum class Method
        {
            NA = 0,
            GSSAPI = 1,
            USERNAMEPASSWORD=2,
            IANA=3,
            RESERVED=0x80,
            NO=0xff
        };
        std::vector<Method> methods;
    
    public:
        template <class F>
        void read_req(F read)
        {
            ver = read(1);
            assert(ver == 5);
            size_t NMETHODS = read(1);
            methods.reserve(NMETHODS);
            assert(NMETHODS > 0 && NMETHODS < 10);
            for (size_t i = 0; i < NMETHODS; i++)
            {
                int method = read(1);
                methods.push_back(Method(method));
            }
        }
    
        template <class F>
        void send_rpy(F write)
        {
            write({ 5 });
            assert(std::find(methods.begin(), methods.end(), Method::NA) != methods.end());
            write({ 0 });
        }
    };
    
    enum class CMDType
    {
        CONNECT=1,
        BIND=2,
        UDP=3,
    };
    
    enum class ATYPType
    {
        IPv4=0x01,
        Domain=0x03,
        IPv6=0x04,
    };
    
    class Cmd
    {
        int VER;
        CMDType CMD;
        int RSV;
        ATYPType ATYP;
        
        union DSTADDR {
            unsigned char ipv4[4];
            char domain[100];
            char ipv6[16];
        }dstAddr;
    
        int dstPort;
    
    public:
        template <class F, class F2>
        void read_req(F read, F2 connect)
        {
            VER = read(1);
            assert(VER == 5);
            CMD = CMDType(read(1));
            RSV = read(1);
            ATYP = ATYPType(read(1));
            switch (ATYP)
            {
            case ATYPType::IPv4:
                for (size_t i = 0; i < 4; i++)
                {
                    dstAddr.ipv4[i] = read(1);
                }
                printf("dest ip: %d,%d,%d,%d
    ", dstAddr.ipv4[0], dstAddr.ipv4[1], dstAddr.ipv4[2], dstAddr.ipv4[3]);
                sprintf(dstAddr.domain, "%d.%d.%d.%d", dstAddr.ipv4[0], dstAddr.ipv4[1], dstAddr.ipv4[2], dstAddr.ipv4[3]);
                break;
            case ATYPType::Domain:
            {
                size_t n = read(1);
                assert(n < 100);
                for (size_t i = 0; i < n; i++)
                {
                    dstAddr.domain[i] = read(1);
                }
                dstAddr.domain[n] = 0;
                printf("dest domain: %s
    ", dstAddr.domain);
            }
                break;
            case ATYPType::IPv6:
                assert(0);
                break;
            default:
                break;
            }
    
            dstPort = read(2);
            printf("dest port: %d
    ", dstPort);
            char sport[100];
            sprintf(sport, "%d", dstPort);
    
            connect(dstAddr.domain, sport);
        }
    
        template <class F>
        void send_rpy(F write)
        {
            write({5 });    // Version: 5
            write({ 0 });    // Results(V5): Succeeded (0)
            write({ 0 });    // Reserved: 0
            write({ 1 });    // Address Type: IPv4 (1)
            write({ 127, 0, 0, 1 }); // Remote Address : 10.0.120.14
            write({ 1,0 });    // Port: 55593
        }
    };
    
    int main()
    {
        try
        {
            boost::asio::io_context io_context;
            tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 10800));
            for (;;)
            {
                tcp::socket socket(io_context);
                acceptor.accept(socket);
                
                auto read =
                    [&socket](int n) ->int {
                    boost::array<char, 1> buf;
                    
                    char ret[4] = { 0 };
                    for (size_t i = 0; i < n; i++)
                    {
                        size_t len = socket.read_some(boost::asio::buffer(buf));
                        ret[i] = buf[0];
                    }
                    int t = *(int*)ret;
                    if (n==1)
                    {
                        return t;
                    }
                    if (n == 2)
                    {
                        return ntohs(t);
                    }
                    if (n == 4)
                    {
                        return ntohl(t);
                    }
                    assert(0);
                };
                char write_data[100];
                int write_len = 0;
                auto write_begin = [&write_len]() {write_len = 0; };
                auto write = [&write_len, &write_data](std::initializer_list<char> args)  {
                    for (char i: args)
                    {
                        write_data[write_len++] = i;
                    }
                };
                auto write_end = [&socket, &write_len, &write_data]() {
                    printf("write %d bytes
    ", write_len);
                    for (size_t i = 0; i < write_len; i++)
                    {
                        printf("%x", write_data[i]);
                    }
                    printf("
    ");
                    socket.write_some(boost::asio::buffer(write_data, write_len));
                };
    
                verify1 v1;
                v1.read_req(read);
                write_begin();
                v1.send_rpy(write);
                write_end();
    
                tcp::resolver::results_type endpoints;
                auto connect = [&io_context, &endpoints](const char *name, const char* port) {
                    tcp::resolver resolver(io_context);
                    endpoints =
                        resolver.resolve(tcp::v4(), name, port);
                };
    
                Cmd cmd;
                cmd.read_req(read, connect);
                write_begin();
                cmd.send_rpy(write);
                write_end();
    
                tcp::socket sock_remote(io_context);
                boost::asio::connect(sock_remote, endpoints);
    
                boost::thread th1([&sock_remote, &socket] {
                    boost::system::error_code ec;
                    while (!ec)
                    {
                        uint8_t data[512];
    
                        size_t len = sock_remote.read_some(boost::asio::buffer(data), ec);
    
                        if (len > 0)
                        {
                            std::cout << "received " << len << " bytes
    ";
                            boost::asio::write(socket, boost::asio::buffer(data, len));
                        }
                    }
                });
                
                boost::system::error_code ec;
                while (!ec)
                {
                    uint8_t data[512];
    
                    size_t len = socket.read_some(boost::asio::buffer(data), ec);
    
                    if (len > 0)
                    {
                        std::cout << "received " << len << " bytes
    ";
                        boost::asio::write(sock_remote, boost::asio::buffer(data, len));
                    }
                }
    
                th1.join();
            }
        }
        catch (std::exception& e)
        {
            std::cerr << e.what() << std::endl;
        }
    
        return 0;
    }
    
    

    异步

    使用boost asio coroutine

    基本思路是把网络层重新实现了一下,协议分析层代码完全一样

    • 协议分析层

      • verify1: 握手. 和同步实现的代码完全一样
      • Cmd:链接目标. 和同步实现的代码完全一样
    • 网络层。用coroutine重新实现

      • read
      • write
      • connect
      • thread:数据转发

      为了便于实现,写了一个session类封装每次链接相关的数据

    #include <ctime>
    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    #include <boost/array.hpp>
    #include <boost/thread.hpp>
    #include <boost/asio/spawn.hpp>
    #include <vector>
    #include <assert.h>
    
    using boost::asio::ip::tcp;
    
    std::string make_daytime_string()
    {
        using namespace std; // For time_t, time and ctime;
        time_t now = time(0);
        return ctime(&now);
    }
    
    class verify1
    {
        int ver;
        
        enum class Method
        {
            NA = 0,
            GSSAPI = 1,
            USERNAMEPASSWORD=2,
            IANA=3,
            RESERVED=0x80,
            NO=0xff
        };
        std::vector<Method> methods;
    
    public:
        template <class F>
        void read_req(F read)
        {
            ver = read(1);
            assert(ver == 5);
            size_t NMETHODS = read(1);
            methods.reserve(NMETHODS);
            assert(NMETHODS > 0 && NMETHODS < 10);
            for (size_t i = 0; i < NMETHODS; i++)
            {
                int method = read(1);
                methods.push_back(Method(method));
            }
        }
    
        template <class F>
        void send_rpy(F write)
        {
            write({ 5 });
            assert(std::find(methods.begin(), methods.end(), Method::NA) != methods.end());
            write({ 0 });
        }
    };
    
    enum class CMDType
    {
        CONNECT=1,
        BIND=2,
        UDP=3,
    };
    
    enum class ATYPType
    {
        IPv4=0x01,
        Domain=0x03,
        IPv6=0x04,
    };
    
    class Cmd
    {
        int VER;
        CMDType CMD;
        int RSV;
        ATYPType ATYP;
        
        union DSTADDR {
            unsigned char ipv4[4];
            char domain[100];
            char ipv6[16];
        }dstAddr;
    
        int dstPort;
    
    public:
        template <class F, class F2>
        void read_req(F read, F2 connect)
        {
            VER = read(1);
            assert(VER == 5);
            CMD = CMDType(read(1));
            RSV = read(1);
            ATYP = ATYPType(read(1));
            switch (ATYP)
            {
            case ATYPType::IPv4:
                for (size_t i = 0; i < 4; i++)
                {
                    dstAddr.ipv4[i] = read(1);
                }
                printf("dest ip: %d,%d,%d,%d
    ", dstAddr.ipv4[0], dstAddr.ipv4[1], dstAddr.ipv4[2], dstAddr.ipv4[3]);
                sprintf(dstAddr.domain, "%d.%d.%d.%d", dstAddr.ipv4[0], dstAddr.ipv4[1], dstAddr.ipv4[2], dstAddr.ipv4[3]);
                break;
            case ATYPType::Domain:
            {
                size_t n = read(1);
                assert(n < 100);
                for (size_t i = 0; i < n; i++)
                {
                    dstAddr.domain[i] = read(1);
                }
                dstAddr.domain[n] = 0;
                printf("dest domain: %s
    ", dstAddr.domain);
            }
                break;
            case ATYPType::IPv6:
                assert(0);
                break;
            default:
                break;
            }
    
            dstPort = read(2);
            printf("dest port: %d
    ", dstPort);
            char sport[100];
            sprintf(sport, "%d", dstPort);
    
            connect(dstAddr.domain, sport);
        }
    
        template <class F>
        void send_rpy(F write)
        {
            write({5 });    // Version: 5
            write({ 0 });    // Results(V5): Succeeded (0)
            write({ 0 });    // Reserved: 0
            write({ 1 });    // Address Type: IPv4 (1)
            write({ 127, 0, 0, 1 }); // Remote Address : 10.0.120.14
            write({ 1,0 });    // Port: 55593
        }
    };
    
    
    // ref: https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/example/cpp03/spawn/echo_server.cpp
    
    class session : public boost::enable_shared_from_this<session>
    {
    public:
        explicit session(boost::asio::io_context& io_context)
            : io_context_(io_context),
            strand_(boost::asio::make_strand(io_context)),
            socket_(io_context),
            sock_remote_(io_context_)
        {
        }
    
        tcp::socket& socket()
        {
            return socket_;
        }
    
        void go()
        {
            boost::asio::spawn(strand_,
                boost::bind(&session::echo,
                    shared_from_this(), boost::placeholders::_1));
        }
    
    private:
        void echo(boost::asio::yield_context yield)
        {
            auto read =
                [this, &yield](int n) ->int {
                boost::array<char, 1> buf;
    
                char ret[4] = { 0 };
                for (size_t i = 0; i < n; i++)
                {
                    size_t len = socket_.async_read_some(boost::asio::buffer(buf), yield);
                    ret[i] = buf[0];
                }
                int t = *(int*)ret;
                if (n == 1)
                {
                    return t;
                }
                if (n == 2)
                {
                    return ntohs(t);
                }
                if (n == 4)
                {
                    return ntohl(t);
                }
                assert(0);
            };
            char write_data[100];
            int write_len = 0;
            auto write_begin = [&write_len]() {write_len = 0; };
            auto write = [&write_len, &write_data](std::initializer_list<char> args) {
                for (char i : args)
                {
                    write_data[write_len++] = i;
                }
            };
            auto write_end = [this, &write_len, &write_data, &yield]() {
                printf("write %d bytes
    ", write_len);
                for (size_t i = 0; i < write_len; i++)
                {
                    printf("%x", write_data[i]);
                }
                printf("
    ");
                boost::asio::async_write(socket_, boost::asio::buffer(write_data, write_len), yield);
            };
    
            verify1 v1;
            v1.read_req(read);
            write_begin();
            v1.send_rpy(write);
            write_end();
    
            tcp::resolver::results_type endpoints;
            boost::system::error_code ec;
            auto connect = [this, &endpoints, &yield, &ec](const char* name, const char* port) {
                tcp::resolver resolver(io_context_);
                endpoints =
                    resolver.async_resolve(tcp::v4(), name, port, yield[ec]);
            };
    
            Cmd cmd;
            cmd.read_req(read, connect);
            if (ec) return;
            write_begin();
            cmd.send_rpy(write);
            write_end();
    
            boost::asio::async_connect(sock_remote_, endpoints, yield[ec]);
            if (ec) return;
    
            boost::asio::spawn(strand_,
                boost::bind(&session::copy_sock_1, shared_from_this(), boost::placeholders::_1));
    
            boost::asio::spawn(strand_,
                boost::bind(&session::copy_sock_2, shared_from_this(), boost::placeholders::_1));
        }
    
        void copy_sock_1(boost::asio::yield_context yield)
        {
            tcp::socket& a = socket_;
            tcp::socket& b = sock_remote_;
            copy_sock(a, b, yield);
        }
    
        void copy_sock_2(boost::asio::yield_context yield)
        {
            tcp::socket& b = socket_;
            tcp::socket& a = sock_remote_;
            copy_sock(a, b, yield);
        }
    
        static void copy_sock(tcp::socket& a, tcp::socket& b, boost::asio::yield_context yield)
        {
            std::cout << "copy_sock 
    ";
            boost::system::error_code ec;
            while (!ec)
            {
                uint8_t data[512];
    
                size_t len = a.async_read_some(boost::asio::buffer(data), yield[ec]);
    
                std::cout << "received " << len << " bytes
    ";
                if (len > 0)
                {
                    b.async_write_some(boost::asio::buffer(data, len), yield[ec]);
                }
            }
        }
        boost::asio::io_context& io_context_;
        boost::asio::strand<boost::asio::io_context::executor_type> strand_;
        tcp::socket socket_;
        tcp::socket sock_remote_;
    };
    
    void do_accept(boost::asio::io_context &io_context, boost::asio::yield_context yield)
    {
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 10800));
    
        for (;;)
        {
            tcp::socket socket(io_context);
            boost::shared_ptr<session> new_session(new session(io_context));
            acceptor.async_accept(new_session->socket(), yield);
            new_session->go();
        }
    }
    int main()
    {
        try
        {
            boost::asio::io_context io_context;
            
            boost::asio::spawn(io_context,
                [&io_context](boost::asio::yield_context yield) {
                do_accept(io_context, yield);
            });
    
            io_context.run();
        }
        catch (std::exception& e)
        {
            std::cerr << e.what() << std::endl;
        }
    
        return 0;
    }
    
    

    如何编译boost

    5.1 Simplified Build From Source

    If you wish to build from source with Visual C++, you can use a simple build procedure described in this section. Open the command prompt and change your current directory to the Boost root directory. Then, type the following commands:

    bootstrap
    .2
    

    The first command prepares the Boost.Build system for use. The second command invokes Boost.Build to build the separately-compiled Boost libraries. Please consult the Boost.Build documentation for a list of allowed options.

    如何cmake引用boost

    set(BOOST_ROOT "G:/_codes/boost_1_74_0_rc2/boost_1_74_0")
    set(Boost_USE_STATIC_LIBS ON)
    find_package(Boost REQUIRED)
    
    include_directories(${Boost_INCLUDE_DIR})
    link_directories(${Boost_LIBRARY_DIR_DEBUG})
    
    

    error LNK2026: module unsafe for SAFESEH image.

    ref

    在Visual Studio 2012 Express Edition中:

    Right-click on your project ->
    Properties -> 
    Configuration Properties ->
    Linker ->
    Advanced and changed "Image Has Safe Exception Handlers" to "No (/SAFESEH:NO)"
    

    python

    如何截获网络封包

    工具 wireshark

    wireshark如何抓取本机包

    ​ 在进行通信开发的过程中,我们往往会把本机既作为客户端又作为服务器端来调试代码,使得本机自己和自己通信。但是wireshark此时是无法抓取到数据包的,需要通过简单的设置才可以。

    ​ 具体方法如下:

    方法一:

    ​ 1.以管理员身份运行cmd

    ​ 2.route add 本机ip mask 255.255.255.255 网关ip

    route add 192.168.1.118 mask 255.255.255.255 192.168.1.1

    ​ 如:route add 172.16.51.115 mask 255.255.255.255 172.16.1.1

    ​ 使用完毕后用route delete 172.16.51.115 mask 255.255.255.255 172.16.1.1删除,否则所有本机报文都经过网卡出去走一圈回来很耗性能。

    ​ 此时再利用wireshark进行抓包便可以抓到本机自己同自己的通信包,这样配置的原因是将发往本机的包发送到网关,而此时wireshark可以捕获到网卡驱动的报文实现抓包。

    ​ 但这样有一个缺点,那就是本地请求的URL的IP只能写本地的IP地址,不能写localhost或127.0.0.1,写localhost或127.0.0.1还是抓不到包。

    方法二:

    ​ windows系统没有提供本地回环网络的接口,用wireshark监控网络的话只能看到经过网卡的流量,看不到访问localhost的流量,因为wireshark在windows系统上默认使用的是WinPcap来抓包的,现在可以用Npcap来替换掉WinPcap,Npcap是基于WinPcap 4.1.3开发的,api兼容WinPcap。

    1.下载安装包

    Npcap项目主页,它采用的是MIT开源协议,Npcap下载

    2.安装

    ​ 安装时要勾选 Use DLT_NULL protocol sa Loopback ... 和 install npcap in winpcap api-compat mode,如下所示。

    img
    如果你已经安装了wireshark, 安装前请先卸载WinPcap。

    img
    如果还提示WinPcap has been detected之类的,那就将C:WindowsSysWOW64下的wpcap.dll修改为wpcap.dll.old,packet.dll修改为packet.dll.old,也可参考:https://nicolask.wordpress.com/2012/09/23/solved-winpcap-4-12-install-error/。

    ​ 当然,如果还没有安装wireshark安装,安装wireshark不要安装WinPcap了。

    ​ 安装完成启动wireshark, 可以看到在网络接口列表中,多了一项Npcap Loopback adapter,这个就是来抓本地回环包的网络接口了,打开后如下图:

    img
    它不仅可以抓URL是localhost的,也可以是127.0.0.1。

    点击查看原始大小图片
    当然,抓本机IP也是完全可以的。

    为啥我死活抓不到socks包?

    你忘记设置端口了

    image-20201002224951835

    socks5协议

    https://guiyongdong.github.io/2017/12/09/Socks5%E4%BB%A3%E7%90%86%E5%88%86%E6%9E%90/

    认证

    通常,socks5代理服务器都会配置在1080端口,他是基于TCP的,客户端要连接到代理服务器,首先要经过三次握手,之后需要和服务器进行认证,格式如下:

    +----+----------+----------+
    |VER | NMETHODS | METHODS  |
    +----+----------+----------+
    | 1  |    1     | 1 to 255 |
    +----+----------+----------+
    
    • VER 字段是当前协议的版本号,也就是5
    • NMETHODS 字段是代表客户端支持的认证方式的个数
    • METHODS 字段代表客户端支持的认证方式,每一位字节表示一种认证方式。

    服务器在客户端发送的认证方式中选择一种进行匹配,返回数据如下:

    +----+----------+
    |VER |  METHODS |
    +----+----------+
    | 1  |    1     |
    +----+----------+
    
    • METHOD 即为服务器端匹配的结果,如果服务器返回的是OxFF,则表明客户端所支持的认证方式,服务器端都不支持。那么认证失败。

    认证方式有如下几种:

    0x00: 无验证需求
    0x01: 通用安全服务应用程序接口(GSSAPI)
    0x02: 用户名/密码(USERNAME/PASSWORD)
    0x03: 至 0x7F IANA 分配(IANA ASSIGNED)
    0x80: 至 0xFE 私人方法保留(RESERVED FOR PRIVATE METHODS)
    0xFF: 无可接受方法(NO ACCEPTABLE METHODS)
    

    下面通过WireShark分析socks5认证过程:

    img

    首先,客户端和服务器的三次握手建立连接,之后,客户端发送认证请求,我们可以看到,客户端发送的数据为:

    • VER:05 即版本为5
    • NMETHODS:02 即支持两种认证方式
    • METHODS: 00 02 两种认证方式分别为 无须认证和用户名密码认证

    img

    服务器回的数据为:

    • VER:05 即版本为5
    • METHODS:00 即和客户端商议使用无须认证方式

    当然,这里我配置的代理服务器没有使用用户名密码认证方式,假如需要此种方式的认证,客户端还需要发送用户名密码进行认证,有兴趣的同学可以试试。

    连接

    认证通过以后,客户端就需要告诉代理,需要它做什么,即需要给代理发命令,具体的格式如下:

    +----+-----+-------+------+----------+----------+
    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  |   1   |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+
    
    • VER: 协议版本
    • CMD: 命令,有三种命令:
    0x01:CONNECT 建立 TCP 连接 
    0x02: BIND 上报反向连接地址
    0x03:关联 UDP 请求
    
    • RSV: 保留字段,值为 0x00
    • ATYP: 地址类型,取值为:
    0x01:IPv4
    0x03: 域名
    0x04:IPv6
    
    • DST.ADDR 目的地,取值随ATYP的类型不同而不同,如下:
    ATYP == 0x01:4 个字节的 IPv4 地址
    ATYP == 0x03:1 个字节表示域名长度,紧随其后的是对应的域名
    ATYP == 0x04:16 个字节的 IPv6 地址
    
    • DST.PORT 目的地端口

    服务器返回的数据如下:

    +----+-----+-------+------+----------+----------+
    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  |   1   |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+
    
    • VER: 版本号
    • 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
    
    • RSV 保留字段
    • ATYP: 地址类型
    • BND.ADDR: 服务器地址
    • BND.PORT: 服务器端口

    CMD

    CONNECT

    CONNECT表示要和目的主机建立TCP连接,在服务器回应中,BND.ADDR包含了关联的IP地址。此处所提供的BND.ADDR通常情况下不同于客户端连接到socks5代理服务器的IP地址,因为有可能代理服务器是一个集群,当然我这里只是一个服务器,所以返回的和代理的IP一样,BND.PORT表示服务器分配的连接到目标主机的端口号,即代理服务器接下来会使用BND.PORT这个端口与目标主机进行TCP通信。

    BIND
    BIND请求被用在那些需要客户机接收到服务器连接的协议中。FTP就是一个众所周知的例子。在实际应用场景中,一般用不到,这里不再细说。

    UDP ASSOCIATE

    此命令表示需要进行UDP转发,BND.ADDR和CONNECT中的含义一样,而BND.PORT表示服务器提供给客户端的UDP转发端口,接下来客户端的所有UDP都需要往代理的此端口发送。

    因为我这次遇到的主要是UDP转发的问题,那么接下就着重分析一下UDP ASSOCIATE。

    UDP ASSOCIATE

    当客户端发送UDP ASSOCIATE命名时,代理服务器会返回相应的断端口,如下:

    img

    服务器响应如下:

    img

    那么,客户端知道服务器地址以后,是如何发送到代理服务器呢?

    一个UDP数据报如下:

    +----+------+------+----------+----------+----------+
    |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |  DATA |
    +----+------+------+----------+----------+----------+
    | 2  |  1 |  1  | Variable |  2    | Variable |
    +----+------+------+----------+----------+----------+
    
    • RSV 占两个字节 即 0x0000
    • FRAG Current fragment number
    • ATYP 目的地址类型
    • DST.ADDR 目的地址
    • DST.PORT 目的端口
    • DATA 真正的数据

    img

    如此,客户端开始不断的发送UDP数据到代理服务器,代理服务器接收到数据后,又会如何呢?

    img

    我们可以看到,客户端与代理之间的数据要比代理与目的主机之间的数据大10个字节,而这10个字节正是socks5协议头。

    完整的UDP转发流程为:

    img

    现在,大家是不是对socks5有了一个全面的认识呢!

    https://i-meto.com/pipe-demo/

  • 相关阅读:
    [Windows]使用nmake命令
    正则表达式
    随机读取数据库
    常用正规表达式应用(收集于网络)
    javascript中使用正则表达式(替换应用)
    摘自网络"浅析UpdatePanel的partial render原理"
    css样式制作相册例子(摘自网络)
    javascript正则表达式常规用法(摘自网络)
    asp.net下的正规表达式替换
    摘自网络创业失败的10个教训(上)
  • 原文地址:https://www.cnblogs.com/cutepig/p/13766113.html
Copyright © 2020-2023  润新知