fbi-proxy 1.6.2 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/release/fbi-proxy-linux-arm64 +0 -0
- package/release/fbi-proxy-linux-x64 +0 -0
- package/release/fbi-proxy-macos-arm64 +0 -0
- package/release/fbi-proxy-macos-x64 +0 -0
- package/release/fbi-proxy-windows-arm64.exe +0 -0
- package/release/fbi-proxy-windows-x64.exe +0 -0
- package/rs/fbi-proxy.rs +63 -39
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/rs/fbi-proxy.rs
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
use clap::{Arg, Command};
|
|
2
2
|
use futures_util::{SinkExt, StreamExt};
|
|
3
|
+
use http_body_util::{BodyExt, Full};
|
|
4
|
+
use hyper::body::{Bytes, Incoming};
|
|
3
5
|
use hyper::header::{HeaderValue, HOST};
|
|
4
|
-
use hyper::
|
|
5
|
-
use hyper::
|
|
6
|
+
use hyper::server::conn::http1;
|
|
7
|
+
use hyper::service::service_fn;
|
|
8
|
+
use hyper::{Request, Response, StatusCode, Uri};
|
|
6
9
|
use hyper_tungstenite::{HyperWebsocket, WebSocketStream};
|
|
10
|
+
use hyper_util::client::legacy::{Client, connect::HttpConnector};
|
|
11
|
+
use hyper_util::rt::TokioIo;
|
|
7
12
|
use log::{error, info};
|
|
8
13
|
use regex::Regex;
|
|
9
14
|
use std::convert::Infallible;
|
|
10
15
|
use std::net::SocketAddr;
|
|
11
16
|
use std::sync::Arc;
|
|
17
|
+
use tokio::net::TcpListener;
|
|
12
18
|
use tokio_tungstenite::connect_async;
|
|
13
19
|
|
|
14
20
|
type BoxError = Box<dyn std::error::Error + Send + Sync>;
|
|
21
|
+
type BoxBody = http_body_util::combinators::BoxBody<Bytes, hyper::Error>;
|
|
15
22
|
|
|
16
23
|
pub struct FBIProxy {
|
|
17
|
-
client: Client<
|
|
24
|
+
client: Client<HttpConnector, BoxBody>,
|
|
18
25
|
number_regex: Regex,
|
|
19
26
|
domain_filter: Option<String>,
|
|
20
27
|
}
|
|
@@ -51,8 +58,12 @@ for subdomains
|
|
|
51
58
|
*/
|
|
52
59
|
impl FBIProxy {
|
|
53
60
|
pub fn new(domain_filter: Option<String>) -> Self {
|
|
61
|
+
let connector = HttpConnector::new();
|
|
62
|
+
let client = Client::builder(hyper_util::rt::TokioExecutor::new())
|
|
63
|
+
.build(connector);
|
|
64
|
+
|
|
54
65
|
Self {
|
|
55
|
-
client
|
|
66
|
+
client,
|
|
56
67
|
number_regex: Regex::new(r"^\d+$").unwrap(),
|
|
57
68
|
domain_filter,
|
|
58
69
|
}
|
|
@@ -131,7 +142,7 @@ impl FBIProxy {
|
|
|
131
142
|
Some((format!("{}:80", host), host.to_string()))
|
|
132
143
|
}
|
|
133
144
|
|
|
134
|
-
pub async fn handle_request(&self,
|
|
145
|
+
pub async fn handle_request(&self, req: Request<Incoming>) -> Result<Response<BoxBody>, BoxError> {
|
|
135
146
|
// Extract host from headers and process according to rules
|
|
136
147
|
let host_header = req
|
|
137
148
|
.headers()
|
|
@@ -157,7 +168,7 @@ impl FBIProxy {
|
|
|
157
168
|
);
|
|
158
169
|
return Ok(Response::builder()
|
|
159
170
|
.status(StatusCode::BAD_GATEWAY)
|
|
160
|
-
.body(
|
|
171
|
+
.body(Full::new(Bytes::from("Bad Gateway: Host not allowed")).map_err(|e| match e {}).boxed())?);
|
|
161
172
|
}
|
|
162
173
|
};
|
|
163
174
|
|
|
@@ -180,14 +191,20 @@ impl FBIProxy {
|
|
|
180
191
|
);
|
|
181
192
|
let target_uri: Uri = target_url.parse()?;
|
|
182
193
|
|
|
194
|
+
// Convert incoming body to a format the client can use
|
|
195
|
+
let (mut parts, incoming_body) = req.into_parts();
|
|
196
|
+
let body = incoming_body.map_err(|e| e).boxed();
|
|
197
|
+
|
|
183
198
|
// Update request URI and headers
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
.insert(HOST, HeaderValue::from_str(&new_host)?);
|
|
199
|
+
parts.uri = target_uri;
|
|
200
|
+
parts.headers.insert(HOST, HeaderValue::from_str(&new_host)?);
|
|
187
201
|
// Preserve content-encoding header to maintain compression
|
|
188
202
|
|
|
203
|
+
// Rebuild the request with the converted body
|
|
204
|
+
let new_req = Request::from_parts(parts, body);
|
|
205
|
+
|
|
189
206
|
// Forward the request
|
|
190
|
-
match self.client.request(
|
|
207
|
+
match self.client.request(new_req).await {
|
|
191
208
|
Ok(response) => {
|
|
192
209
|
// Preserve content-encoding header in response to maintain compression
|
|
193
210
|
let status = response.status();
|
|
@@ -199,7 +216,10 @@ impl FBIProxy {
|
|
|
199
216
|
original_uri,
|
|
200
217
|
status.as_u16()
|
|
201
218
|
);
|
|
202
|
-
|
|
219
|
+
// Convert the response body back to BoxBody
|
|
220
|
+
let (parts, body) = response.into_parts();
|
|
221
|
+
let boxed_body = body.map_err(|e| e).boxed();
|
|
222
|
+
Ok(Response::from_parts(parts, boxed_body))
|
|
203
223
|
}
|
|
204
224
|
Err(e) => {
|
|
205
225
|
error!(
|
|
@@ -212,17 +232,17 @@ impl FBIProxy {
|
|
|
212
232
|
);
|
|
213
233
|
Ok(Response::builder()
|
|
214
234
|
.status(StatusCode::BAD_GATEWAY)
|
|
215
|
-
.body(
|
|
235
|
+
.body(Full::new(Bytes::from("FBIPROXY ERROR")).map_err(|e| match e {}).boxed())?)
|
|
216
236
|
}
|
|
217
237
|
}
|
|
218
238
|
}
|
|
219
239
|
|
|
220
240
|
async fn handle_websocket_upgrade(
|
|
221
241
|
&self,
|
|
222
|
-
req: Request<
|
|
242
|
+
req: Request<Incoming>,
|
|
223
243
|
target_host: &str,
|
|
224
244
|
_new_host: &str, // Currently not used for WebSocket connections, but kept for consistency
|
|
225
|
-
) -> Result<Response<
|
|
245
|
+
) -> Result<Response<BoxBody>, BoxError> {
|
|
226
246
|
let uri = req.uri().clone();
|
|
227
247
|
let ws_url = format!(
|
|
228
248
|
"ws://{}{}",
|
|
@@ -240,7 +260,7 @@ impl FBIProxy {
|
|
|
240
260
|
error!("WS :ws:{} => :ws:{}{} 502 ({})", target_host, target_host, uri, e);
|
|
241
261
|
return Ok(Response::builder()
|
|
242
262
|
.status(StatusCode::BAD_GATEWAY)
|
|
243
|
-
.body(
|
|
263
|
+
.body(Full::new(Bytes::from("WebSocket connection failed")).map_err(|e| match e {}).boxed())?);
|
|
244
264
|
}
|
|
245
265
|
};
|
|
246
266
|
|
|
@@ -253,7 +273,9 @@ impl FBIProxy {
|
|
|
253
273
|
});
|
|
254
274
|
|
|
255
275
|
info!("WS :ws:{} => :ws:{}{} 101", target_host, target_host, uri);
|
|
256
|
-
|
|
276
|
+
let (parts, body) = response.into_parts();
|
|
277
|
+
let boxed_body = body.map_err(|_: std::convert::Infallible| unreachable!()).boxed();
|
|
278
|
+
Ok(Response::from_parts(parts, boxed_body))
|
|
257
279
|
}
|
|
258
280
|
}
|
|
259
281
|
|
|
@@ -304,39 +326,27 @@ async fn handle_websocket_forwarding(
|
|
|
304
326
|
}
|
|
305
327
|
|
|
306
328
|
async fn handle_connection(
|
|
307
|
-
req: Request<
|
|
329
|
+
req: Request<Incoming>,
|
|
308
330
|
proxy: Arc<FBIProxy>,
|
|
309
|
-
) -> Result<Response<
|
|
331
|
+
) -> Result<Response<BoxBody>, Infallible> {
|
|
310
332
|
match proxy.handle_request(req).await {
|
|
311
333
|
Ok(response) => Ok(response),
|
|
312
334
|
Err(e) => {
|
|
313
335
|
error!("Request handling error: {}", e);
|
|
314
336
|
Ok(Response::builder()
|
|
315
337
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
316
|
-
.body(
|
|
338
|
+
.body(Full::new(Bytes::from("Internal Server Error")).map_err(|e| match e {}).boxed())
|
|
317
339
|
.unwrap())
|
|
318
340
|
}
|
|
319
341
|
}
|
|
320
342
|
}
|
|
321
343
|
|
|
322
|
-
pub async fn start_proxy_server(port: u16) -> Result<(), BoxError> {
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
pub async fn start_proxy_server_with_host(host: &str, port: u16) -> Result<(), BoxError> {
|
|
327
|
-
start_proxy_server_with_options(host, port, None).await
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
pub async fn start_proxy_server_with_options(host: &str, port: u16, domain_filter: Option<String>) -> Result<(), BoxError> {
|
|
344
|
+
pub async fn start_proxy_server(host: Option<&str>, port: u16, domain_filter: Option<String>) -> Result<(), BoxError> {
|
|
345
|
+
let host = host.unwrap_or("127.0.0.1");
|
|
331
346
|
let addr: SocketAddr = format!("{}:{}", host, port).parse()?;
|
|
332
347
|
let proxy = Arc::new(FBIProxy::new(domain_filter.clone()));
|
|
333
348
|
|
|
334
|
-
let
|
|
335
|
-
let proxy = proxy.clone();
|
|
336
|
-
async move { Ok::<_, Infallible>(service_fn(move |req| handle_connection(req, proxy.clone()))) }
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
let server = Server::bind(&addr).serve(make_svc);
|
|
349
|
+
let listener = TcpListener::bind(addr).await?;
|
|
340
350
|
|
|
341
351
|
info!("FBI Proxy server running on http://{}", addr);
|
|
342
352
|
println!("FBI Proxy listening on: http://{}", addr);
|
|
@@ -350,11 +360,25 @@ pub async fn start_proxy_server_with_options(host: &str, port: u16, domain_filte
|
|
|
350
360
|
|
|
351
361
|
info!("Features: HTTP proxying + WebSocket forwarding + Port encoding + Domain filtering");
|
|
352
362
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
363
|
+
loop {
|
|
364
|
+
let (stream, _) = listener.accept().await?;
|
|
365
|
+
let io = TokioIo::new(stream);
|
|
366
|
+
let proxy = proxy.clone();
|
|
356
367
|
|
|
357
|
-
|
|
368
|
+
tokio::task::spawn(async move {
|
|
369
|
+
let service = service_fn(move |req| handle_connection(req, proxy.clone()));
|
|
370
|
+
|
|
371
|
+
if let Err(err) = http1::Builder::new()
|
|
372
|
+
.preserve_header_case(true)
|
|
373
|
+
.title_case_headers(true)
|
|
374
|
+
.serve_connection(io, service)
|
|
375
|
+
.with_upgrades()
|
|
376
|
+
.await
|
|
377
|
+
{
|
|
378
|
+
error!("Error serving connection: {:?}", err);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
358
382
|
}
|
|
359
383
|
|
|
360
384
|
fn main() {
|
|
@@ -453,7 +477,7 @@ TRY RUN:
|
|
|
453
477
|
"Starting FBI-Proxy on {}:{} with domain filter: {:?}",
|
|
454
478
|
host, port, domain_filter
|
|
455
479
|
);
|
|
456
|
-
if let Err(e) =
|
|
480
|
+
if let Err(e) = start_proxy_server(Some(host), port, domain_filter).await {
|
|
457
481
|
error!("Failed to start proxy server: {}", e);
|
|
458
482
|
}
|
|
459
483
|
});
|