Skip to content

Commit b2accb6

Browse files
authored
Merge pull request #25 from th7nder/t-17
Add listening interface and port selection
2 parents e58a94b + 001c679 commit b2accb6

File tree

3 files changed

+136
-58
lines changed

3 files changed

+136
-58
lines changed

mitm_proxy/src/main.rs

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
1+
mod managed_proxy;
12
mod mitm_proxy;
23
mod requests;
34

4-
use std::{
5-
sync::mpsc::{ sync_channel},
6-
thread, net::SocketAddr,
7-
};
8-
95
use crate::mitm_proxy::MitmProxy;
106

117
use eframe::{
128
egui::{self, CentralPanel, Vec2},
13-
run_native, App
9+
run_native, App,
1410
};
15-
use proxyapi::proxy::Proxy;
16-
use tokio::runtime::Runtime;
1711

1812
static X: f32 = 980.;
1913
static Y: f32 = 960.0;
@@ -25,25 +19,18 @@ static PADDING: f32 = 20.;
2519

2620
impl App for MitmProxy {
2721
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
28-
2922
ctx.request_repaint();
3023

3124
self.manage_theme(ctx);
3225

3326
self.render_top_panel(ctx, frame);
34-
35-
CentralPanel::default().show(ctx, |ui|{
36-
self.render_columns(ui);
27+
28+
CentralPanel::default().show(ctx, |ui| {
29+
self.render_columns(ui);
3730
});
3831
}
3932
}
4033

41-
async fn shutdown_signal() {
42-
tokio::signal::ctrl_c()
43-
.await
44-
.expect("Failed to install CTRL+C signal handler");
45-
}
46-
4734
fn load_icon(path: &str) -> eframe::IconData {
4835
let (icon_rgba, icon_width, icon_height) = {
4936
let image = image::open(path)
@@ -66,24 +53,9 @@ fn main() {
6653
native_options.initial_window_size = Some(Vec2::new(X, Y));
6754
native_options.icon_data = Some(load_icon("./assets/logo.png"));
6855

69-
// create the app with listener false
70-
// update listener when it is true
71-
72-
let (tx, rx) = sync_channel(1);
73-
let rt = Runtime::new().unwrap();
74-
let addr = SocketAddr::new([127,0,0,1].into(), 8100);
75-
76-
thread::spawn(move || {
77-
rt.block_on( async move {
78-
if let Err(e) = Proxy::new(addr, Some(tx.clone())).start(shutdown_signal()).await{
79-
eprintln!("Error running proxy on {:?}: {e}", addr);
80-
}
81-
})
82-
});
83-
8456
run_native(
8557
"Man In The Middle Proxy",
8658
native_options,
87-
Box::new(|cc| Box::new(MitmProxy::new(cc, rx))),
59+
Box::new(|cc| Box::new(MitmProxy::new(cc))),
8860
)
8961
}

mitm_proxy/src/managed_proxy.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::{
2+
net::SocketAddr,
3+
sync::mpsc::Receiver,
4+
thread::{self, JoinHandle},
5+
};
6+
7+
use proxyapi::{Proxy, ProxyHandler};
8+
use tokio::{runtime::Runtime, sync::oneshot::Sender};
9+
10+
use crate::requests::RequestInfo;
11+
12+
pub struct ManagedProxy {
13+
rx: Receiver<ProxyHandler>,
14+
close: Option<Sender<()>>,
15+
thread: Option<JoinHandle<()>>,
16+
}
17+
18+
impl ManagedProxy {
19+
pub fn new(addr: SocketAddr) -> ManagedProxy {
20+
let (tx, rx) = std::sync::mpsc::sync_channel(1);
21+
let (close_tx, close_rx) = tokio::sync::oneshot::channel();
22+
23+
let rt = Runtime::new().unwrap();
24+
25+
let thread = thread::spawn(move || {
26+
rt.block_on(async move {
27+
if let Err(e) = Proxy::new(addr, Some(tx.clone()))
28+
.start(async move {
29+
let _ = close_rx.await;
30+
})
31+
.await
32+
{
33+
eprintln!("Error running proxy on {:?}: {e}", addr);
34+
}
35+
})
36+
});
37+
38+
ManagedProxy {
39+
rx,
40+
close: Some(close_tx),
41+
thread: Some(thread),
42+
}
43+
}
44+
45+
pub fn try_recv_request(&mut self) -> Option<RequestInfo> {
46+
match self.rx.try_recv() {
47+
Ok(l) => {
48+
let (request, response) = l.to_parts();
49+
Some(RequestInfo::new(request, response))
50+
}
51+
_ => None,
52+
}
53+
}
54+
}
55+
56+
impl Drop for ManagedProxy {
57+
fn drop(&mut self) {
58+
if let Some(t) = self.thread.take() {
59+
if let Some(close) = self.close.take() {
60+
let _ = close.send(());
61+
}
62+
t.join().expect("Couldn't gracefully shutdown the proxy.")
63+
}
64+
}
65+
}

mitm_proxy/src/mitm_proxy.rs

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
1-
use std::{
2-
fmt::{Display},
3-
sync::mpsc::Receiver,
4-
};
1+
use std::{fmt::Display, net::SocketAddr};
52

63
use crate::{
4+
managed_proxy::ManagedProxy,
75
requests::{InfoOptions, RequestInfo},
86
PADDING,
97
};
108

119
use eframe::{
1210
egui::{
1311
self, ComboBox, FontData, FontDefinitions, FontFamily, Grid, Layout, RichText, ScrollArea,
14-
Style, TextStyle::*, TopBottomPanel, Visuals,
12+
Style, TextEdit, TextStyle::*, TopBottomPanel, Visuals,
1513
},
16-
epaint::FontId,
14+
epaint::{Color32, FontId},
1715
Frame,
1816
};
1917
use egui_extras::{Column, TableBuilder};
20-
use proxyapi::{hyper::Method, *};
18+
use proxyapi::hyper::Method;
2119
use serde::{Deserialize, Serialize};
2220

2321
#[derive(Serialize, Deserialize)]
@@ -77,6 +75,7 @@ struct MitmProxyState {
7775
selected_request: Option<usize>,
7876
selected_request_method: MethodFilter,
7977
detail_option: InfoOptions,
78+
listen_on: String,
8079
}
8180

8281
impl MitmProxyState {
@@ -85,6 +84,7 @@ impl MitmProxyState {
8584
selected_request: None,
8685
selected_request_method: MethodFilter::All,
8786
detail_option: InfoOptions::Request,
87+
listen_on: "127.0.0.1:8100".to_string(),
8888
}
8989
}
9090
}
@@ -93,11 +93,11 @@ pub struct MitmProxy {
9393
requests: Vec<RequestInfo>,
9494
config: MitmProxyConfig,
9595
state: MitmProxyState,
96-
rx: Receiver<ProxyHandler>,
96+
proxy: Option<ManagedProxy>,
9797
}
9898

9999
impl MitmProxy {
100-
pub fn new(cc: &eframe::CreationContext<'_>, rx: Receiver<ProxyHandler>) -> Self {
100+
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
101101
Self::configure_fonts(cc);
102102
let config: MitmProxyConfig = confy::load("MitmProxy", None).unwrap_or_default();
103103
let state = MitmProxyState::new();
@@ -106,7 +106,7 @@ impl MitmProxy {
106106
requests: vec![],
107107
config,
108108
state,
109-
rx,
109+
proxy: None,
110110
}
111111
}
112112

@@ -117,6 +117,23 @@ impl MitmProxy {
117117
}
118118
}
119119

120+
fn start_proxy(&mut self, addr: SocketAddr) {
121+
assert!(self.proxy.is_none());
122+
123+
self.proxy = Some(ManagedProxy::new(addr));
124+
self.requests = vec![];
125+
}
126+
127+
fn stop_proxy(&mut self) {
128+
assert!(self.proxy.is_some());
129+
130+
self.proxy.take();
131+
}
132+
133+
fn is_running(&self) -> bool {
134+
return self.proxy.is_some();
135+
}
136+
120137
fn configure_fonts(cc: &eframe::CreationContext<'_>) {
121138
let mut fonts = FontDefinitions::default();
122139

@@ -255,19 +272,14 @@ impl MitmProxy {
255272
});
256273
}
257274

258-
pub fn update_requests(&mut self) -> Option<RequestInfo> {
259-
match self.rx.try_recv() {
260-
Ok(l) => {
261-
let (request,response) = l.to_parts();
262-
Some(RequestInfo::new(request,response))
263-
},
264-
_ => None,
265-
}
266-
}
267-
268275
pub fn render_columns(&mut self, ui: &mut egui::Ui) {
269-
if let Some(request) = self.update_requests() {
270-
self.requests.push(request);
276+
if !self.is_running() {
277+
return;
278+
}
279+
if let Some(ref mut proxy) = self.proxy {
280+
if let Some(request) = proxy.try_recv_request() {
281+
self.requests.push(request);
282+
}
271283
}
272284

273285
if let Some(i) = self.state.selected_request {
@@ -292,6 +304,37 @@ impl MitmProxy {
292304
pub fn render_top_panel(&mut self, ctx: &egui::Context, _frame: &mut Frame) {
293305
TopBottomPanel::top("top_panel").show(ctx, |ui| {
294306
ui.add_space(PADDING);
307+
egui::menu::bar(ui, |ui| -> egui::InnerResponse<_> {
308+
ui.with_layout(Layout::left_to_right(eframe::emath::Align::Min), |ui| {
309+
if !self.is_running() {
310+
TextEdit::singleline(&mut self.state.listen_on).show(ui);
311+
312+
match self.state.listen_on.parse::<SocketAddr>() {
313+
Ok(addr) => {
314+
let start_button = ui.button("▶").on_hover_text("Start");
315+
if start_button.clicked() {
316+
self.start_proxy(addr);
317+
}
318+
}
319+
Err(_err) => {
320+
ui.label(
321+
RichText::new("Provided invalid IP address")
322+
.color(Color32::RED),
323+
);
324+
}
325+
};
326+
} else {
327+
TextEdit::singleline(&mut self.state.listen_on)
328+
.interactive(false)
329+
.show(ui);
330+
let stop_button = ui.button("■").on_hover_text("Stop");
331+
if stop_button.clicked() {
332+
self.stop_proxy();
333+
}
334+
}
335+
})
336+
});
337+
295338
egui::menu::bar(ui, |ui| -> egui::InnerResponse<_> {
296339
ui.with_layout(Layout::left_to_right(eframe::emath::Align::Min), |ui| {
297340
let clean_btn = ui.button("🚫").on_hover_text("Clear");
@@ -326,14 +369,12 @@ impl MitmProxy {
326369
});
327370

328371
ui.with_layout(Layout::right_to_left(eframe::emath::Align::Min), |ui| {
329-
330372
let theme_btn = ui
331373
.button(match self.config.dark_mode {
332374
true => "🔆",
333375
false => "🌙",
334376
})
335377
.on_hover_text("Toggle theme");
336-
337378

338379
if theme_btn.clicked() {
339380
self.config.dark_mode = !self.config.dark_mode

0 commit comments

Comments
 (0)