码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 用Rust手把手编写一个Proxy(代理), 动工


    合集 - wmproxy(16)
    1.用Rust手把手编写一个Proxy(代理), 动工09-19
    2.用Rust手把手编写一个Proxy(代理), UDP绑定篇09-213.5. 用Rust手把手编写一个Proxy(代理), 通讯协议建立, 为内网穿透做准备09-284.6. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 通讯协议源码解读篇09-305.7. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP及TCP内网穿透原理及运行篇10-046.8. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP改造篇之HPACK原理10-087. 用Rust手把手编写一个Proxy(代理), 准备篇, 动手造轮子09-168.9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示例, 了解http2头信息如何处理10-099.10. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP内网穿透支持修改头信息10-1010.11. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 实现健康检查10-1211.12. 用Rust手把手编写一个wmproxy(代理,内网穿透等), TLS的双向认证信息及token验证10-1412.13. 从零开始编写一个类nginx工具, HTTP中的压缩gzip,deflate,brotli算法10-1713.14. 从零开始编写一个类nginx工具, HTTP文件服务器的实现过程及参数10-1914.15. 从零开始编写一个类nginx工具, 如果将nginx.conf转成yaml,toml,json会怎么样10-2015.16. 从零开始编写一个类nginx工具, 反向代理upstream源码实现10-2316.17. 从零开始编写一个类nginx工具, Rust中一些功能的实现10-24
    收起

    用Rust手把手编写一个Proxy(代理), 动工

    项目 ++wmproxy++

    gitee 传送门
    github 传送门

    设计流程图

    Http
    直达
    Https
    Socks5
    客户端
    代理端
    代理服务端
    服务端

    代理端和代理服务端之间可用自有格式来实现多路复用以减少连接的建立断开的开销,目前暂未实现代理服务端。

    类结构

    • proxy.rs 负责代理结构的存储,监听类型,监听地址,是否有父级地址,认证账号密码等。
    • flag.rs 监听类型的二进制结构,可同时支持多结构比较http/https/socks5,如果解析http失败则尝试socks5格式,从而实现多种代理方式的同时支持
    • http.rs http及https代理的实现,如果解析失败则返回ProxyError::Continue,并把已经读取的数据带回,以便后续解析
    • socks5.rs socks5的代理实现,如果数据正确,则均在此处进行转发,解析失败返回Continue

    命令行解析

    使用Commander对命令行的的数据处理,如-p 8090,-b 127.0.0.1,完整的命令行如wmproxy -p 8090,则可在8090端口上实现http及https的转发,代码示例

    let command = Commander::new()
    .version(&env!("CARGO_PKG_VERSION").to_string())
    .usage("-b 127.0.0.1 -p 8090")
    .usage_desc("use http proxy")
    .option_list(
    "-f, --flag [value]",
    "可兼容的方法, 如http https socks5",
    None,
    )
    .option_int("-p, --port [value]", "listen port", Some(8090))
    .option_str(
    "-b, --bind [value]",
    "bind addr",
    Some("0.0.0.0".to_string()),
    )
    .parse_env_or_exit();
    let listen_port: u16 = command.get_int("p").unwrap() as u16;
    let listen_host = command.get_str("b").unwrap();

    启动入口

    启动通过tokio的异步协议进行数据的处理,逻辑均在tokio::spawn的异步函数中,所有针对句柄数据的读取写入均由异步完成,从而实现高效率的处理。

    while let Ok((mut inbound, _)) = listener.accept().await {
    tokio::spawn(async move {
    // tcp的连接被移动到该协程中,我们只要专注的处理该stream即可
    })
    }

    HTTP代理

    如果该代理信息配置支持http/https则会尝试进行http解析,代码实现在proxy.rs中的process方法,

    pub async fn process(mut inbound: TcpStream) -> ProxyResult<()> {
    let request = webparse::Request::new();
    // 通过该方法解析标头是否合法, 若是partial(部分)则继续读数据
    // 若解析失败, 则表示非http协议能处理, 则抛出错误
    match request.parse_buffer(&mut buffer.clone()) {
    }
    }

    该方法会循环的读取客户端的内容,如果内容为

    GET / HTTP/1.1\r\nHost: wwww.baidu.com\r\n\r\n

    这表示该请求为普通的http代理,我们解析完HTTP的头文件信息,得出包含的头信息,如果无法解析完整的地址(域名加端口或者ip加端口),则返回错误,无法处理该http信息。

    成功
    成功
    不合法关闭
    连接失败关闭
    双向
    客户端
    代理
    读取头信息
    取得地址
    连接目标地址
    写入头信息
    双向通道

    注意:客户端和服务端之前可能会存在大数据上传下载的情况,超过百兆数据的上传下载,所以我们为了减少序列化带来的性能损失和保证在低内存能正确运行,不做http的完整解析,仅仅只处理http头信息。

    curl测试

    export http_proxy=http://127.0.0.1:8090
    curl http://www.baidu.com -I

    可以正常的返回

    HTTP/1.1 200 OK...

    HTTPS代理

    https处理是在http的基础在在额外解析connect协议来实现, 代理是客户端优先给代理发送connect协议,比如访问https://www.baidu.com那么先优先发如下消息。

    CONNECT www.baidu.com:443 HTTP/1.1\r\n
    Host: www.baidu.com:443\r\n\r\n

    如果收到HTTP的CONNECT的方法则表示他是https的代理协议,那么此时对PATH提示的地址进行连接,连接成功后只需对该连接和客户端做双向绑定即可实现HTTPS代理协议。

    curl测试

    export https_proxy=http://127.0.0.1:8090
    curl https://www.baidu.com -I

    可以正常的返回两次,因为在connect的时候要求代理返回一次数据,另一次是https服务器返回,故而显示g

    HTTP/1.1 200 OK
    HTTP/1.1 200 OK
    ...

    socks5协议

    socks5由rfc1928进行定义
    代码实现在socks5.rs中的process方法实现
    因为在处理socks5之前可能进行过http的尝试,所以socket中的内容已经被读出了一部分,在处理时则带上了Option,表示预读的内容。
    在socks5中通常需要预读一个字节来获取后续的长度,比如NMethod,或者用户名长度等,所以我们定义了函数

    /// 读取至少长度为size的大小的字节数, 如果足够则返回Ok(())
    pub async fn read_len(stream: &mut T, buffer: &mut BinaryMut, size: usize) -> ProxyResult<()>
    where
    T: AsyncRead + Unpin {
    }

    这里的stream用的是泛型,只要具有异步读的类型都可以

    保证已读内容须不少于多少字节数,然后再进行数据的预处理。
    根据我们是否传用用户密码信息来确定socks5的验证方式,如果我们传入了用户密码,如果客户端不支持2的验证方式,则返回(0xFF)表示无验证方法。

    curl http://www.baidu.com --socks5 127.0.0.1:8090
    ## curl: (97) No authentication method was acceptable.

    验证成功或者无需验证后

    连接成功
    读取失败
    连接失败
    双向
    验证成功
    读取地址
    连接地址
    双向通道
    返回失败

    双向通道建立后,客户端已和服务器能正常的TCP操作,包括Http/Https/Websocket/自定义tcp信息,代理直到一方关闭则正常后续关闭。

    错误处理方法

    这里主要说明如何多协议兼容处理代理协议。以下定义的Continue协议包含了一个已读的字节表和当前的Tcp连接。

    pub enum ProxyError {
    /// 该错误发生协议不可被解析,则尝试下一个协议
    Continue((Option, TcpStream)),
    }

    例如在http里协议解决头失败,

    // 此处clone为浅拷贝,不确定是否一定能解析成功,不能影响偏移
    match request.parse_buffer(&mut buffer.clone()) {
    Err(_) => {
    return Err(ProxyError::Continue((Some(buffer), inbound)));
    }
    }

    则返回当前已读的buffer和tcp连接,且游标为初始位置,buffer并未被读取过。下个解析器可以拿到完整的数据进行解析。

  • 相关阅读:
    基于PHP+MySQL动漫社管理系统的设计与实现
    Linux Learnning
    Vue iView DatePicker时间组件踩坑
    简单易懂的 Go 泛型使用和实现原理介绍
    苹果认证Apple Store Connenct api的使用
    网络套接字(UDP和TCP编程)
    js:react使用zustand实现状态管理
    Tableau 合集2:Table Extension通过python做词云图
    R统计绘图-线性混合效应模型详解(理论、模型构建、检验、选择、方差分解及结果可视化)
    前端开发新趋势:Web3、区块链与虚拟现实
  • 原文地址:https://www.cnblogs.com/luojiawaf/p/17713872.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号