ctf_pwn/io/stdio/
ncurses_bridge.rs

1use crate::io::stdio::{is_stop_terminal, is_terminate_process, TerminalBridge, TerminalResult};
2use crate::io::{ansi, TerminalError};
3use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
4use crossterm::*;
5use std::io::ErrorKind::TimedOut;
6use std::io::stdout;
7use std::sync::atomic::{AtomicBool, Ordering};
8use std::sync::Arc;
9use std::time::Duration;
10use crossterm::terminal::*;
11use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
12use tokio::join;
13use tokio::task::JoinError;
14
15pub struct NcursesTerminalBridge {}
16
17async fn read_task<R>(reader: &mut R, stop_signal: Arc<AtomicBool>) -> TerminalResult<()>
18where
19    R: AsyncRead + Send + Unpin,
20{
21    let mut buffer = [0; 1024];
22    loop {
23        if stop_signal.load(Ordering::SeqCst) {
24            break;
25        }
26
27        let n = match reader.read(&mut buffer).await {
28            Ok(n) if n == 0 => break, //EOF
29            Ok(n) => n,
30            Err(e) if e.kind() == TimedOut => continue,
31            Err(e) => return Err(e.into()),
32        };
33
34        let mut stdout = tokio::io::stdout();
35        terminal::disable_raw_mode().unwrap();
36        stdout.write_all(&buffer[..n]).await?;
37        stdout.flush().await?;
38        terminal::enable_raw_mode().unwrap();
39    }
40    Ok(())
41}
42
43async fn write_ansi_command<W: AsyncWrite + Send + Unpin, T: Command>(
44    writer: &mut W,
45    command: T,
46) -> Result<usize, TerminalError> {
47    let mut ansi_command = String::new();
48    command.write_ansi(&mut ansi_command)?;
49    let size = writer.write(ansi_command.as_bytes()).await?;
50    Ok(size)
51}
52
53async fn send_key<W>(
54    writer: &mut W,
55    stop_signal: Arc<AtomicBool>,
56    key_event: KeyEvent,
57) -> TerminalResult<()>
58where
59    W: AsyncWrite + Send + Unpin,
60{
61    if is_terminate_process(key_event) {
62        return Err(TerminalError::Terminate);
63    }
64
65    if is_stop_terminal(key_event) {
66        stop_signal.store(true, Ordering::SeqCst);
67        return Ok(());
68    }
69
70    if key_event.kind != KeyEventKind::Press && key_event.kind != KeyEventKind::Repeat {
71        return Ok(());
72    }
73
74    match key_event.code {
75        KeyCode::Char(c) => {
76            let mut buf = [0; 4]; // UTF-8 encoding of a char may use up to 4 bytes
77            let bytes = c.encode_utf8(&mut buf);
78            writer.write_all(bytes.as_bytes()).await?;
79        }
80        KeyCode::Left => {
81            write_ansi_command(writer, ansi::Left).await?;
82        }
83        KeyCode::Right => {
84            write_ansi_command(writer, ansi::Right).await?;
85        }
86        KeyCode::Up => {
87            write_ansi_command(writer, ansi::Up).await?;
88        }
89        KeyCode::Down => {
90            write_ansi_command(writer, ansi::Down).await?;
91        }
92        KeyCode::Delete => {
93            write_ansi_command(writer, ansi::Del).await?;
94        }
95        KeyCode::Backspace => {
96            write_ansi_command(writer, ansi::Backspace).await?;
97        }
98        KeyCode::Esc => {
99            write_ansi_command(writer, ansi::Esc).await?;
100        }
101        KeyCode::Enter => {
102            write_ansi_command(writer, ansi::Enter).await?;
103        }
104        KeyCode::Home => {
105            write_ansi_command(writer, ansi::Home).await?;
106        }
107        KeyCode::End => {
108            write_ansi_command(writer, ansi::End).await?;
109        }
110        KeyCode::PageUp => {
111            write_ansi_command(writer, ansi::PgUp).await?;
112        }
113        KeyCode::PageDown => {
114            write_ansi_command(writer, ansi::PgDn).await?;
115        }
116        KeyCode::F(no) => {
117            write_ansi_command(writer, ansi::F(no)).await?;
118        }
119        _ => {}
120    };
121
122    writer.flush().await?;
123
124    Ok(())
125}
126
127async fn write_task<W>(writer: &mut W, stop_signal: Arc<AtomicBool>) -> TerminalResult<()>
128where
129    W: AsyncWrite + Send + Unpin,
130{
131    loop {
132        if stop_signal.load(Ordering::SeqCst) {
133            return Ok(());
134        }
135
136        if let Ok(true) = event::poll(Duration::from_millis(0)) {
137            match event::read() {
138                Ok(event::Event::Key(key_event)) => {
139                    send_key(writer, stop_signal.clone(), key_event).await?;
140                }
141                _ => {}
142            }
143        }
144    }
145}
146
147impl TerminalBridge for NcursesTerminalBridge {
148    async fn bridge<R: AsyncRead + Send + Unpin, W: AsyncWrite + Send + Unpin>(
149        reader: &mut R,
150        writer: &mut W,
151    ) {
152        let reader_ptr = reader as *mut R as usize;
153        let writer_ptr = writer as *mut W as usize;
154
155        execute!(stdout(), EnterAlternateScreen).unwrap();
156        terminal::enable_raw_mode().unwrap();
157
158        let stop_signal = Arc::new(AtomicBool::new(false));
159        let read_stop_signal = stop_signal.clone();
160
161        let reader_task = tokio::spawn(async move {
162            let reader_ptr = reader_ptr as *mut R;
163            let reader = unsafe { &mut *reader_ptr };
164            let res = read_task(reader, read_stop_signal.clone()).await;
165            read_stop_signal.store(true, Ordering::SeqCst);
166            res
167        });
168
169        let writer_task = tokio::spawn(async move {
170            let writer_ptr = writer_ptr as *mut W;
171            let writer = unsafe { &mut *writer_ptr };
172            let res = write_task(writer, stop_signal.clone()).await;
173            stop_signal.store(true, Ordering::SeqCst);
174            res
175        });
176
177        let (read_res, write_res) = join!(reader_task, writer_task);
178
179        terminal::disable_raw_mode().unwrap();
180
181        execute!(stdout(), LeaveAlternateScreen).unwrap();
182
183        if let Ok(Err(TerminalError::Terminate)) = read_res
184        {
185            std::process::exit(130); //SIGINT
186        }
187        else if  let Ok(Err(TerminalError::Terminate)) = write_res
188        {
189            std::process::exit(130); //SIGINT
190        }
191    }
192}