7. 从零用Rust编写正反向代理, HTTP及TCP内网穿透原理及运行篇
wmproxy
wmproxy是由Rust编写,已实现http/https代理,socks5代理, 反向代理,静态文件服务器,内网穿透,配置热更新等, 后续将实现websocket代理等,同时会将实现过程分享出来, 感兴趣的可以一起造个轮子法
项目 ++wmproxy++
gite: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
内网、公网
内网:也叫做局域网,通常指单一的网络环境。例如你家里的路由器网络、网吧、公司网络、学校网络。网络大小不定,内网中的主机可以互联互通,但是越出这个局域网访问,就无法访问该网络中的主机。
公网:就是互联网,其实也可以看做一个扩大版的内网,比如叫城际网,省域网,国网。有单独的公网IP,任何其它地址可以访问网络的可以直接访问该IP,从而实现服务。
为什么要内网穿透
内网限制
- IP不固定,通过家庭网,手机4G/5G访问的出口地址都是动态的,每次连接都会变化
- 运营商通常会做NAT转化,从而实际上你访问的出口地址其实也是一个内网地址,如通常
https://www.baidu.com/s?wd=ip查询地址 - 常用端口无法使用,如80/443这类标准端口被直接限制不能使用。
公网优缺点
- 服务器贵,带宽贵
- IP固定,所有端口均可开放
- 带宽稳定,基本上所有高防机房或者云厂商都能提供稳定的带宽
内网穿透的场景
场景1:开发人员本地调试接口
描述:线上项目有问题或者有某些新功能,必须进行Debug进行调试和测试。
特点:本地调试、网速要求低、需要HTTP或者HTTPS协议。
需求:必须本地,必须HTTP[S]网址。
场景2:公司或者家里的本地存储或者公司内部系统
描述:如外出进行工作,或者本地有大量的私有数据(敏感不适合上云),但是自己必须得进行访问,如git服务或者照片服务等
特点:需要远程能随时随地的访问,访问内容不确定,但是需要能提供
需求:要相对比较稳定的线路,但是带宽相对要求较低
场景3:私有服务器和小伙伴开黑
描述:把自己的电脑做服务器,有时候云上的主机配置相对较高点的一个月费用极高,所以需要本地做私有服务器,或者把自己当做一台训练机
特点:对稳定性要求不用太高的,可以提供相应的服务
TCP内网穿透的原理
内网IP无法直接被访问,所以此时需求
- 内网服务器
- 公网服务器,有公网IP
此时网络如下,如此外部用户就能访问到内网服务器的数据,此时内网穿透客户端及服务端是保持长连接以方便进行推送,本质上是长链接在转发数据而实现穿透功能
Rust实现内网穿透
wmproxy一款简单易用的内网穿透工具,简单示例如下:
客户端相关
客户端配置client.yaml
# 连接服务端地址
server: 127.0.0.1:8091
# 连接服务端是否加密
ts: true# 内网映射配置的数组
mappings:#将localhost的域名转发到本地的127.0.0.1:8080- name: webmode: httplocal_addr: 127.0.0.1:8080domain: localhost#将tcp的流量无条件转到127.0.0.1:8080- name: tcpmode: tcplocal_addr: 127.0.0.1:8080domain:
启动客户端
wmproxy -c config/client.yaml
服务端相关
服务端配置server.yaml
#绑定的ip地址
bind_addr: 127.0.0.1:8091
#代理支持的功能,1为http,2为https,4为socks5
flag: 7
#内网映射http绑定地址
map_http_bind: 127.0.0.1:8001
#内网映射tcp绑定地址
map_tcp_bind: 127.0.0.1:8002
#内网映射https绑定地址
map_https_bind: 127.0.0.1:8003
#内网映射的公钥证书,为空则是默认证书
map_cert:
#内网映射的私钥证书,为空则是默认证书
map_key:
#接收客户端是为是加密客户端
tc: true
#当前服务模式,server为服务端,client为客户端
mode: server
启动服务端
wmproxy -c config/server.yaml
测试实现
在本地的8080端口上启动了一个简单的http文件服务器
http-server .
http测试
此时,8001的端口是http内网穿透通过服务端映射到客户端,并指向到8080端口,此时若访问
http://127.0.0.1:8001则会显示
http映射是根据域名做映射此时我们的域名是127.0.0.1,所以直接返回404无法访问
此时若访问http://localhost:8001,结果如下
我们就可以判定我们的内网转发成功了。
tcp测试
tcp就是在该端口上的流量无条件转发到另一个端口上,此时我们可以预测tcp映射与域名无关,我们在8002上转发到了8080上,此时我们访问
http://127.0.0.1:8002和http://localhost:8002都可以得到一样的结果
此时tcp转发成功
源码实现
因为TLS连接与协议无关,只要把普通的TCP转成TLS,剩下的均和普通连接一样处理即可,那么,此时我们只需要处理TCP和HTTP的请求转发即可。
监听
在程序启动的时候看我们是否配置了相应的http/https/tcp的内网穿透转发,如果有我们对相应的端口做监听,此时如果我们是https转发,要配置相应的证书,将会对TcpStream升级为TlsStream<TcpStream>
let http_listener = if let Some(ls) = &self.option.map_http_bind {Some(TcpListener::bind(ls).await?)
} else {None
};
let mut https_listener = if let Some(ls) = &self.option.map_https_bind {Some(TcpListener::bind(ls).await?)
} else {None
};let map_accept = if https_listener.is_some() {let map_accept = self.option.get_map_tls_accept().await.ok();if map_accept.is_none() {let _ = https_listener.take();}map_accept
} else {None
};
let tcp_listener = if let Some(ls) = &self.option.map_tcp_bind {Some(TcpListener::bind(ls).await?)
} else {None
};
转发相关代码,主要在两个类里,分别为trans/http.rs和trans/tcp.rs
在
http里面需要预处理相关的头文件消息,
X-Forwarded-For添加IP信息,从而使内网可以知道访问的IP来源Host,重写Host信息,让内网端如果配置负载均衡可以正确的定位到位置Server,重写Server信息,让内网可以明确知道这个服务端的类型
http转发源码
以下为部分代码,后续将进行比较正规的HTTP服务,以适应HTTP2
pub async fn process<T>(self, mut inbound: T) -> Result<(), ProxyError<T>>
whereT: AsyncRead + AsyncWrite + Unpin,
{let mut request;let host_name;let mut buffer = BinaryMut::new();loop {// 省略读信息request = webparse::Request::new();// 通过该方法解析标头是否合法, 若是partial(部分)则继续读数据// 若解析失败, 则表示非http协议能处理, 则抛出错误// 此处clone为浅拷贝,不确定是否一定能解析成功,不能影响偏移match request.parse_buffer(&mut buffer.clone()) {Ok(_) => match request.get_host() {Some(host) => {host_name = host;break;}None => {if !request.is_partial() {Self::err_server_status(inbound, 503).await?;return Err(ProxyError::UnknownHost);}}},// 数据不完整,还未解析完,等待传输Err(WebError::Http(HttpError::Partial)) => {continue;}Err(e) => {Self::err_server_status(inbound, 503).await?;return Err(ProxyError::from(e));}}}// 取得相关的host数据,对内网的映射端做匹配,如果未匹配到返回错误,表示不支持{let mut is_find = false;let read = self.mappings.read().await;for v in &*read {if v.domain == host_name {is_find = true;}}if !is_find {Self::not_match_err_status(inbound, "no found".to_string()).await?;return Ok(());}}// 有新的内网映射消息到达,通知客户端建立对内网指向的连接进行双向绑定,后续做正规的http服务以支持拓展let create = ProtCreate::new(self.sock_map, Some(host_name));let (stream_sender, stream_receiver) = channel::<ProtFrame>(10);let _ = self.sender_work.send((create, stream_sender)).await;// 创建传输端进行绑定let mut trans = TransStream::new(inbound, self.sock_map, self.sender, stream_receiver);trans.reader_mut().put_slice(buffer.chunk());trans.copy_wait().await?;// let _ = copy_bidirectional(&mut inbound, &mut outbound).await?;Ok(())
}
tcp转发源码
tcp处理相对比较简单,因为我们无法确定协议里是哪个类型的源码,所以对我们来说,就是单纯的把接收的数据完全转发到新的端口里。以下是部分源码
pub async fn process<T>(self, inbound: T) -> Result<(), ProxyError<T>>
whereT: AsyncRead + AsyncWrite + Unpin,
{// 寻找是否有匹配的tcp转发协议,如果有,则进行转发,如果没有则丢弃数据{let mut is_find = false;let read = self.mappings.read().await;for v in &*read {if v.mode == "tcp" {is_find = true;}}if !is_find {log::warn!("not found tcp client trans");return Ok(());}}// 通知客户端数据进行连接的建立,客户端的tcp配置只能存在有且只有一个,要不然无法确定转发源let create = ProtCreate::new(self.sock_map, None);let (stream_sender, stream_receiver) = channel::<ProtFrame>(10);let _ = self.sender_work.send((create, stream_sender)).await;let trans = TransStream::new(inbound, self.sock_map, self.sender, stream_receiver);trans.copy_wait().await?;Ok(())
}
到此部分细节已基本调通,后续将优化http的处理相关,以方便支持http的头信息重写和tcp的错误信息将写入正确的日志,以方便进行定位。
相关文章:
7. 从零用Rust编写正反向代理, HTTP及TCP内网穿透原理及运行篇
wmproxy wmproxy是由Rust编写,已实现http/https代理,socks5代理, 反向代理,静态文件服务器,内网穿透,配置热更新等, 后续将实现websocket代理等,同时会将实现过程分享出来ÿ…...
UE4.27-UE5.1设置打包Android环境
打包Android配置文件 1. 配置打包Android的SDK需求文件位于下面文件中: 2. 指定了对应的SDK环境变量名字以及NDK需求等: UE4.27-UE5.1--脚本自动配置 安装前提 1. 务必关闭虚幻编辑器和Epic Games Launcher,以确保NDK组件的安装或引擎环境…...
MySQL授权密码
mysql> crate databases school charcter set utf8; Query OK, 1 row affected, 1 warning (0.00 sec) 2.在school数据库中创建Student和Score表 mysql> use school Database changed mysql> create table student-> -> (id int(10) primary key auto_incremen…...
0X05
打开题目 点击完登录和注册都没有什么反应,所以先扫一下看看 在出现admin.php后就截止了,访问看看,进入后台。。 尝试一下弱口令 admin/12345 或者是demo/demo 设计中-自定义->右上角导出主题 找到一个导出的点,下载了一个1.zip压缩包…...
Doris优化总结
1 查看QueryProfile 利用查询执行的统计结果,可以更好的帮助我们了解Doris的执行情况,并有针对性的进行相应Debug与调优工作。 FE将查询计划拆分成为Fragment下发到BE进行任务执行。BE在执行Fragment时记录了运行状态时的统计值,并将Fragment执行的统计信息输出到日志之中。…...
案例059:基于微信小程序的在线投稿系统
文末获取源码 开发语言:Java 框架:SSM JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序…...
利用STM32内置Bootloader实现USB DFU固件升级
本文将介绍如何利用STM32内置的Bootloader来实现USB DFU(Device Firmware Upgrade)固件升级功能。首先,我们会介绍USB DFU的原理和工作流程。然后,我们将详细讲解如何配置STM32芯片以支持USB DFU,并提供相应的代码示例…...
Centos7如何安装MySQL
目录 一、卸载mysql 二、安装mysql 注:本文主要是看了这位大佬安装MySQL,才想着写一篇记录一下。 一、卸载mysql 安装mysql之前一定要将之前安装的mysql相关文件删除干净,防止出现错误。 (1)关闭mysql 开启了mysql就…...
VR远程带看,助力线下门店线上化转型“自救”
VR远程带看,因自身高效的沉浸式在线沟通功能,逐渐走进了大众的视野。身临其境的线上漫游体验以及实时同屏互联的新型交互模式,提升了商家同用户之间的沟通效率,进一步实现了远程线上一对一、一对多的同屏带看,用户足不…...
算法通关村第十七关-白银挑战贪心算法高频题目
大家好我是苏麟 , 今天说说贪心算法的高频题目 . 大纲 区间问题判断区间是否重叠合并区间插入区间 区间问题 判断区间是否重叠 描述 : 给定一个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间intervalsl[i] [start, end] ,请你…...
【数据结构】动态规划(Dynamic Programming)
一.动态规划(DP)的定义: 求解决策过程(decision process)最优化的数学方法。 将多阶段决策过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。 二.动态规划的基本思想: …...
Redis key过期删除机制实现分析
文章目录 前言Redis key过期淘汰机制惰性删除机制定时扫描删除机制 前言 当我们创建Redis key时,可以通过expire命令指定key的过期时间(TTL),当超过指定的TTL时间后,key将会失效。 那么当key失效后,Redis会立刻将其删除么&#…...
ElasticSearch 谈谈分词与倒排索引的原理
ElasticSearch是一个基于Lucene的搜索服务器。Lucene是Java的一个全文检索工具包,而ElasticSearch则是一个分布式搜索和分析引擎。下面,我们将详细讨论ElasticSearch中的分词和倒排索引的原理。 分词: 在ElasticSearch中,分词是…...
【Java】Java8重要特性——Lambda函数式编程以及Stream流对集合数据的操作
【Java】Java8重要特性——Lambda函数式编程以及Stream流对集合数据的操作 前言Lambda函数式编程Stream流对集合数据操作(一)创建Stream流(二)中间操作之filter(三)中间操作之map(四)…...
大话数据结构-查找-散列表查找(哈希表)
注:本文同步发布于稀土掘金。 8 散列表查找(哈希表) 8.1 定义 散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。查找时,根据这个确定的对应关系找到给…...
持续集成交付CICD:Sonarqube自动更新项目质量配置
目录 一、实验 1.Sonarqube手动自定义质量规则并指定项目 2.Sonarqube自动更新项目质量配置 一、实验 1.Sonarqube手动自定义质量规则并指定项目 (1)自定义质量规则 ①新配置 ②更多激活规则③根据需求激活相应规则④已新增配置 ⑤ 查看 &#x…...
Linux设置Docker自动创建Nginx容器脚本
文章目录 前言一、本地新建脚本二、复制本地脚本到服务器三、执行服务器脚本总结如有启发,可点赞收藏哟~ 前言 一、本地新建脚本 在本地新建nginx-generator.sh脚本文件,并保存以下内容 主要动态定义两个变量(容器名称/服务器本地文件名、端…...
技术博客:Vue中各种混淆用法汇总
技术博客:Vue中各种混淆用法汇总 摘要 本文主要介绍了在Vue中使用的一些常见混淆用法,包括new Vue()、export default {}、createApp()、Vue.component、Vue3注册全局组件、Vue.use()等,以及如何使用混淆器对代码进行加固,保护应…...
【python】Python生成GIF动图,多张图片转动态图,pillow
pip install pillow 示例代码: from PIL import Image, ImageSequence# 图片文件名列表 image_files [car.png, detected_map.png, base64_image_out.png]# 打开图片 images [Image.open(filename) for filename in image_files]# 设置输出 GIF 文件名 output_g…...
python/matlab图像去雾/去雨综述
图像去雾和去雨是计算机视觉领域的两个重要任务,旨在提高图像质量和可视化效果。本文将综述图像去雾和去雨的算法、理论以及相关项目代码示例。 一、图像去雾算法 基于暗通道先验的方法: 这是广泛应用于图像去雾的经典算法之一。该方法基于一个观察&…...
ARM Neoverse-V3架构解析与性能优化实战
1. ARM Neoverse-V3架构概览作为Arm公司面向基础设施领域的最新处理器IP,Neoverse-V3代表了当前服务器级处理器的顶尖设计水平。我在实际芯片开发中多次接触该架构,其设计哲学可概括为:通过精细化微架构控制实现性能与能效的完美平衡。1.1 指…...
ElevenLabs葡萄牙语语音优化黄金7步法:含音频波形对比图、MOS评分提升路径与合规性审查checklist
更多请点击: https://intelliparadigm.com 第一章:ElevenLabs葡萄牙语语音优化的底层逻辑与技术边界 ElevenLabs 对葡萄牙语(尤其是巴西葡萄牙语,pt-BR)的语音合成并非简单地复用英语模型微调,而是基于多阶…...
Lingoose框架实战:构建智能客服工单处理AI工作流
1. 项目概述:从“Lingo”到“Goose”,一个AI应用编排框架的诞生如果你最近在折腾大语言模型应用,尤其是想把OpenAI、Anthropic这些API的能力整合到自己的业务流程里,那你大概率已经体会过那种“胶水代码”的烦恼了。今天要聊的这个…...
PAC技术演进与核心趋势:从多域控制到边缘智能的工业自动化平台
1. 项目概述:为什么今天还要聊PAC?如果你在工业自动化、楼宇控制或者任何涉及逻辑控制的领域工作,那么“PAC”这个词对你来说应该不陌生。但很多时候,它就像一个熟悉的陌生人——大家好像都知道它,但真要细说它现在发展…...
Proof Engine:简化零知识证明开发,降低区块链应用门槛
1. 项目概述:Proof Engine,一个为现代开发者设计的证明引擎如果你和我一样,在构建需要复杂逻辑验证、状态证明或零知识证明(ZKP)相关应用时,常常感到头疼——工具链复杂、学习曲线陡峭、不同框架间的兼容性…...
017、Docker在TinyML开发中的应用
017 Docker在TinyML开发中的应用 从一次“环境地狱”说起 上个月帮团队调一个STM32上的TinyML推理延迟问题,模型是MobileNetV2量化版,在开发板上跑得好好的,换到同事的Ubuntu 20.04机器上编译,死活链接不上CMSIS-NN库。折腾半天发现他系统里默认的arm-none-eabi-gcc版本是…...
【仅剩47份】Midjourney湿版摄影风格训练数据包(含1851–1889年原始湿版扫描图谱×236张+ICC色彩配置文件×5):精准匹配V6.6新渲染引擎底层纹理采样逻辑
更多请点击: https://intelliparadigm.com 第一章:湿版摄影风格的历史溯源与数字再生价值 湿版摄影(Wet Plate Collodion Process)诞生于1851年,由英国科学家弗雷德里克斯科特阿彻(Frederick Scott Archer…...
低配置电脑适配 OpenClaw 搭配 Ollama 流畅使用技巧
前置准备 获取小龙虾open claw一键安装包(www.totom.top)并安装电脑已成功安装运行 OpenClaw 客户端,顶部 Gateway 状态保持在线网络正常,可顺利访问 Ollama 官方网站电脑空余磁盘空间充足,本地 AI 模型占用体积较大提…...
Mod Engine 2完全指南:告别游戏模组安装烦恼的终极解决方案
Mod Engine 2完全指南:告别游戏模组安装烦恼的终极解决方案 【免费下载链接】ModEngine2 Runtime injection library for modding Souls games. WIP 项目地址: https://gitcode.com/gh_mirrors/mo/ModEngine2 还在为传统游戏模组安装的繁琐流程而烦恼吗&…...
如何高效使用Diablo Edit2:暗黑破坏神II存档修改的全面解决方案
如何高效使用Diablo Edit2:暗黑破坏神II存档修改的全面解决方案 【免费下载链接】diablo_edit Diablo II Character editor. 项目地址: https://gitcode.com/gh_mirrors/di/diablo_edit 想要在暗黑破坏神II中打造理想角色,却苦于漫长的刷怪过程&a…...


