1use std::collections::HashMap;
12use std::convert::Infallible;
13use std::sync::Arc;
14
15use actix::{Addr, Recipient};
16use http::uri::PathAndQuery;
17use hyper::client::HttpConnector;
18use hyper::server::conn::{AddrStream, Http};
19use hyper::service::{make_service_fn, service_fn};
20use hyper::upgrade::Upgraded;
21use hyper::{Body, Client, Method, Request, Response, Server};
22use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
23use inn_common::genca::CertAuthority;
24use log::{debug, error, info};
25use tokio::net::TcpStream;
26use tokio_rustls::TlsAcceptor;
27
28use crate::server::ProxyServer;
29use crate::{ToProxyServer, WsHttpReq};
30#[derive(Clone)]
31pub struct Proxy {
32 ca: Arc<CertAuthority>,
33 client: Client<HttpsConnector<HttpConnector>>,
34 server: Recipient<ToProxyServer>,
35}
36impl Proxy {
44 pub async fn start_proxy(ip: &str, cacert: &str, cakey: &str, server: Addr<ProxyServer>) {
45 let addr = ip.parse().expect("invalid ip");
46 let https = HttpsConnectorBuilder::new()
47 .with_webpki_roots()
48 .https_or_http()
49 .enable_http1()
50 .enable_http2()
51 .build();
52 let client = Client::builder()
53 .http1_title_case_headers(true)
54 .http1_preserve_header_case(true)
55 .build(https);
56 let ca = Arc::new(CertAuthority::new(cacert.to_string(), cakey.to_string()));
57 let make_service = make_service_fn(move |_conn: &AddrStream| {
58 let client = client.clone();
59 let server = server.clone();
60 let ca = Arc::clone(&ca);
61 async move {
62 Ok::<_, Infallible>(service_fn(move |req| {
63 Proxy {
64 ca: Arc::clone(&ca),
65 client: client.clone(),
66 server: server.clone().recipient(),
67 }
68 .proxy(req)
69 }))
70 }
71 });
72
73 let server = Server::bind(&addr)
74 .http1_preserve_header_case(true)
75 .http1_title_case_headers(true)
76 .serve(make_service);
77
78 info!("Https proxy server, Listening on http://{}", addr);
79
80 if let Err(e) = server.await {
81 eprintln!("server error: {}", e);
82 }
83 }
84
85 async fn proxy(self, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
86 if Method::CONNECT == req.method() {
87 if let Some(addr) = Proxy::host_addr(req.uri()) {
101 tokio::task::spawn(async move {
102 match hyper::upgrade::on(req).await {
103 Ok(upgraded) => {
104 let host: Vec<&str> = addr.split(':').collect();
105 if Proxy::mitm_match(host[0], host[1]) {
106 let server_config = self.ca.dynamic_gen_cert(host[0]).await;
108 match TlsAcceptor::from(server_config).accept(upgraded).await {
109 Ok(stream) => {
110 if let Err(e) = self.serve_https(stream).await {
111 error!("addr = {} serve_https error = {}", addr, e);
112 }
113 }
114 Err(e) => {
115 error!("addr = {} TlsAcceptor error = {}", addr, e);
116 }
117 }
118 } else {
119 debug!("addr = {}, tunnel", addr);
120 let _ = Proxy::tunnel(upgraded, &addr).await;
121 }
122 }
123 Err(e) => error!("addr = {}, upgrade error: {}", addr, e),
124 }
125 });
126 Ok(Response::new(Body::empty()))
127 } else {
128 error!("CONNECT host is not socket addr: {:?}", req.uri());
129 let mut resp = Response::new(Body::from("CONNECT must be to a socket address"));
130 *resp.status_mut() = http::StatusCode::BAD_REQUEST;
131 Ok(resp)
132 }
133 } else {
134 self.request(req).await
135 }
136 }
137
138 async fn serve_https(
139 self,
140 stream: tokio_rustls::server::TlsStream<Upgraded>,
141 ) -> Result<(), hyper::Error> {
142 let service = service_fn(|mut req| {
143 if req.version() == http::Version::HTTP_10 || req.version() == http::Version::HTTP_11 {
144 let authority = req
145 .headers()
146 .get(http::header::HOST)
147 .expect("Host is a required header")
148 .to_str()
149 .expect("Failed to convert host to str");
150
151 let uri = http::uri::Builder::new()
152 .scheme(http::uri::Scheme::HTTPS)
153 .authority(authority)
154 .path_and_query(
155 req.uri()
156 .path_and_query()
157 .unwrap_or(&PathAndQuery::from_static("/"))
158 .to_owned(),
159 )
160 .build()
161 .expect("Failed to build URI");
162
163 let (mut parts, body) = req.into_parts();
164 parts.uri = uri;
165 req = Request::from_parts(parts, body)
166 };
167 self.clone().request(req)
169 });
170
171 Http::new()
172 .serve_connection(stream, service)
173 .with_upgrades()
174 .await
175 }
176 fn host_addr(uri: &http::Uri) -> Option<String> {
177 uri.authority().map(|auth| auth.to_string())
178 }
179 async fn request(self, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
182 let uri = req.uri().clone();
183 let headers = req.headers().clone();
184 let body = format!("{:?}", req.body());
185 let method = req.method().as_str().to_string();
186 let ver = format!("{:?}", req.version());
187 let host = Proxy::host_addr(req.uri()).unwrap();
188 let rs = self.client.request(req).await;
189 match &rs {
190 Ok(resp) => {
191 let mut h = HashMap::new();
192 for (k, v) in &headers {
193 h.insert(k.to_string(), v.to_str().unwrap().to_string());
194 }
195 let mut resp_h = HashMap::new();
196 for (k, v) in resp.headers() {
197 resp_h.insert(k.to_string(), v.to_str().unwrap().to_string());
198 }
199
200 self.server
201 .do_send(ToProxyServer::HttpReq(Box::new(WsHttpReq {
202 id: "0".to_string(),
203 uri: uri.to_string(),
204 headers: h,
205 status: resp.status().as_u16(),
206 error: "".to_owned(),
207 method,
208 req_body: body,
209 server_ip: "".to_string(),
210 protocol: ver,
211 host,
212 resp_headers: resp_h,
213 resp_body: format!("{:?}", resp.body()),
214 time: "".to_string(),
215 })));
216 }
217 Err(_e) => {
218 }
225 }
226 rs
227 }
228 async fn tunnel(mut upgraded: Upgraded, addr: &str) -> std::io::Result<()> {
231 let mut server = TcpStream::connect(addr).await?;
233 let _ = tokio::io::copy_bidirectional(&mut upgraded, &mut server).await?;
234 Ok(())
235 }
236 fn mitm_match(host: &str, port: &str) -> bool {
237 matches!(
238 (host, port),
239 ("github.com", _) | ("www.github.com", _) | ("baidu.com", _) | ("www.baidu.com", _)
240 )
241 }
242}