1.用Rust手把手编写一个Proxy(代理), 动工2.用Rust手把手编写一个Proxy(代理), UDP绑定篇3.5. 用Rust手把手编写一个Proxy(代理), 通讯协议建立, 为内网穿透做准备4.6. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 通讯协议源码解读篇5.7. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP及TCP内网穿透原理及运行篇6.8. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP改造篇之HPACK原理7. 用Rust手把手编写一个Proxy(代理), 准备篇, 动手造轮子8.9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示例, 了解http2头信息如何处理9.10. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP内网穿透支持修改头信息10.11. 用Rust手把手编写一个wmproxy(代理,内网穿透等), 实现健康检查11.12. 用Rust手把手编写一个wmproxy(代理,内网穿透等), TLS的双向认证信息及token验证12.13. 从零开始编写一个类nginx工具, HTTP中的压缩gzip,deflate,brotli算法13.14. 从零开始编写一个类nginx工具, HTTP文件服务器的实现过程及参数14.15. 从零开始编写一个类nginx工具, 如果将nginx.conf转成yaml,toml,json会怎么样15.16. 从零开始编写一个类nginx工具, 反向代理upstream源码实现16.17. 从零开始编写一个类nginx工具, Rust中一些功能的实现17.18. 从零开始编写一个类nginx工具, 主动式健康检查源码实现18.19. 从零开始编写一个类nginx工具, 配置数据的热更新原理及实现19.25. 干货系列从零用Rust编写正反向代理,序列化之serde是如何工作的20.27. 干货系列从零用Rust编写正反向代理,Rust中日志库的应用基础准备21.28. 干货系列从零用Rust编写正反向代理,项目日志的源码实现22.30. 干货系列从零用Rust编写正反向代理,HTTP的组装之旅(中间件)23.29. 干货系列从零用Rust编写正反向代理,异步回调(async trait)的使用24.26. 干货系列从零用Rust编写正反向代理,如何发布Rust项目到Docker25.32. 干货系列从零用Rust编写正反向代理,关于堆和栈以及如何解决stack overflow26.33. 干货系列从零用Rust编写正反向代理,关于HTTP客户端代理的源码实现27.34. 干货系列从零用Rust编写负载均衡及代理,异步测试在Rust中的实现28.35. 干货系列从零用Rust编写负载均衡及代理,代理服务器的源码升级改造
29.36. 干货系列从零用Rust编写负载均衡及代理,内网穿透中内网代理的实现
30.37. 干货系列从零用Rust编写负载均衡及代理,负载均衡中try_files实现31.38. 干货系列从零用Rust编写负载均衡及代理,负载均衡中ip通行与禁止32.39. 干货系列从零用Rust编写负载均衡及代理,正则及格式替换33.40. 干货系列从零用Rust编写负载均衡及代理,websocket的实现34.41. 干货系列从零用Rust编写负载均衡及代理,websocket与tcp的映射,WS与TCP互转35.42 干货系列从零用Rust编写负载均衡及代理,wmproxy中配置tcp转websocket36.43 干货系列从零用Rust编写负载均衡及代理,内网穿透方案完整部署37.44从零开始用Rust编写nginx,命令行参数的设计与解析及说明38.45从零开始用Rust编写nginx,静态文件服务器竟然还有这些细节39.46从零开始用Rust编写nginx,数据还能这么传,多层代理(IP多级代理)搭建40.47从零开始用Rust编写nginx,配对还有这么多要求!负载均衡中的路径匹配41.48从零开始用Rust编写nginx,搭建一个简单又好看官方网站42.49从零开始用Rust编写nginx,我竟然在同一个端口上绑定了多少IP43.50从零开始用Rust编写nginx,原来TLS证书还可以这么申请44.51从零开始用Rust编写nginx,江湖救急,TLS证书快过期了wmproxy
wmproxy
已用Rust
实现http/https
代理, socks5
代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket
代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子
项目地址
国内: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
项目设计目标
- HTTP转发
- HTTPS转发(证书在服务器,内网为HTTP)
- TCP转发(纯粹的TCP转发,保持原样的协议)
- PROXY转发(服务端接收数据,内网的客户端当成PROXY客户端,相当于逆向访问内网服务器,[新增])
实现方案
服务端提供客户端的连接端口,可加密Tls
,可双向加密mTls
,可账号密码认证,客户端连接服务端的端口等待数据的处理。主要有两个类服务端CenterServer
及客户端CenterClient
一些细节可以参考第5篇,第6篇,第10篇,第12篇,有相关的内网穿透的细节。
内网代理的实现
- 首先添加一种模式
#[serde_as] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct MappingConfig { /// 其它字段.... // 添加模块proxy pub mode: String, }
- 添加内网代理监听端口
#[serde_as] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProxyConfig { /// 其它字段.... pub(crate) map_http_bind: Option, pub(crate) map_https_bind: Option, pub(crate) map_tcp_bind: Option, // 新加代理接口监听字段 pub(crate) map_proxy_bind: Option, - }
目前端口做唯一绑定,后续可根据配置动态响应相应的数据。
- 做映射
由于代理和tcp类似,服务端均不做任务处理,只需将数据完全转发给客户端处理即可
pub async fn server_new_prxoy(&mut self, stream: TcpStream) -> ProxyResult<()> { let trans = TransTcp::new( self.sender(), self.sender_work(), self.calc_next_id(), self.mappings.clone(), ); tokio::spawn(async move { if let Err(e) = trans.process(stream, "proxy").await { log::warn!("内网穿透:转发Proxy转发时发生错误:{:?}", e); } }); return Ok(()); }
- 客户端处理
客户端将映射流转化成VirtualStream
,把它当成一个虚拟流,然后逻辑均用代理的来处理
let (virtual_sender, virtual_receiver) = channel::(10); map.insert(p.sock_map(), virtual_sender); if mapping.as_ref().unwrap().is_proxy() { let stream = VirtualStream::new( p.sock_map(), sender.clone(), virtual_receiver, ); let (flag, username, password, udp_bind) = ( option.flag, option.username.clone(), option.password.clone(), option.udp_bind.clone(), ); tokio::spawn(async move { // 处理代理的能力 let _ = WMCore::deal_proxy( stream, flag, username, password, udp_bind, ) .await; }); }
VirtualStream
是一个虚拟出一个流连接,并实现AsyncRead及AsyncRead,可以和流一样正常操作,这也是Trait
而不是继承的好处之一,定义就可以比较简单:
pub struct VirtualStream { // sock绑定的句柄 id: u32, // 收到数据通过sender发送给中心端 sender: PollSender, // 收到中心端的写入请求,转成write receiver: Receiver, // 读取的数据缓存,将转发成ProtFrame read: BinaryMut, // 写的数据缓存,直接写入到stream下,从ProtFrame转化而来 write: BinaryMut, }
- 设计
ProxyServer
类
统一的代理服务类,剥离相关代码,使代码更清晰
/// 代理服务器类, 提供代理服务 pub struct ProxyServer { flag: Flag, username: Option<String>, password: Option<String>, udp_bind: Option, headers: Vec, }
- 代理
HTTP
头信息的重写
在HTTP
类中添加相关代码以支持头信息重写
impl Operate { fn deal_request(&self, req: &mut RecvRequest) -> ProtResult<()> { if let Some(headers) = &self.headers { // 复写Request的头文件信息 Helper::rewrite_request(req, headers); } Ok(()) } fn deal_response(&self, res: &mut RecvResponse) -> ProtResult<()> { if let Some(headers) = &self.headers { // 复写Request的头文件信息 Helper::rewrite_response(res, headers); } Ok(()) } }
内网代理流程图:
这样子我们就以代理的方式拥有了所有的内网HTTP相关服务的访问权限。可以简化我们网络的结构。
自动化测试
内网穿透的自动化测试在 tests/mapping
将自动构建内网客户端服务,外网服务端服务做测试,以下部分代码节选:
#[tokio::test] async fn run_test() { let local_server_addr = run_server().await.unwrap(); let addr = "127.0.0.1:0".parse().unwrap(); let proxy = ProxyConfig::builder() .bind_addr(addr) .map_http_bind(Some(addr)) .map_https_bind(Some(addr)) .map_tcp_bind(Some(addr)) .map_proxy_bind(Some(addr)) .center(true) .mode("server".to_string()) .into_value() .unwrap(); let (server_addr, http_addr, https_addr, tcp_addr, proxy_addr, _sender) = run_mapping_server(proxy).await.unwrap(); let mut mapping = MappingConfig::new( "test".to_string(), "http".to_string(), "soft.wm-proxy.com".to_string(), vec![], ); mapping.local_addr = Some(local_server_addr); let mut mapping_tcp = MappingConfig::new( "tcp".to_string(), "tcp".to_string(), "soft.wm-proxy.com".to_string(), vec![], ); mapping_tcp.local_addr = Some(local_server_addr); let mut mapping_proxy = MappingConfig::new( "proxy".to_string(), "proxy".to_string(), "soft.wm-proxy.com1".to_string(), vec![ ConfigHeader::new(wmproxy::HeaderOper::Add, false, "from_proxy".to_string(), "mapping".to_string()) ], ); mapping_proxy.local_addr = Some(local_server_addr); let proxy = ProxyConfig::builder() .bind_addr(addr) .server(Some(server_addr)) .center(true) .mode("client".to_string()) .mapping(mapping) .mapping(mapping_tcp) .mapping(mapping_proxy) .into_value() .unwrap(); let _client_sender = run_mapping_client(proxy).await.unwrap(); fn do_build_req(url: &str, method: &str, body: &Vec<u8>) -> Request { let body = BinaryMut::from(body.clone()); Request::builder() .method(method) .url(&*url) .body(Body::new_binary(body)) .unwrap() } { let url = &*format!("http://{}/", local_server_addr); let client = Client::builder() // .http2(false) .http2_only(true) .add_proxy(&*format!("http://{}", proxy_addr.unwrap())).unwrap() .connect(&*url) .await .unwrap(); let mut res = client .send_now(do_build_req(url, "GET", &vec![])) .await .unwrap(); let mut result = BinaryMut::new(); res.body_mut().read_all(&mut result).await; // 测试头信息来确认是否来源于代理 assert_eq!(res.headers().get_value(&"from_proxy"), &"mapping"); assert_eq!(result.remaining(), HELLO_WORLD.as_bytes().len()); assert_eq!(result.as_slice(), HELLO_WORLD.as_bytes()); assert_eq!(res.version(), Version::Http2); } }
小结
内网代理可以实现不想暴露太多信息给外部,但是又能提供内部的完整信息支持,相当于建立了一条可用的HTTP通道。可以在有这方面需求的人优化网络结构。
点击 [关注],[在看],[点赞] 是对作者最大的支持