Skip to content

Commit cc4600c

Browse files
committed
refactor: enhance Crypt struct with key storage and implement Clone trait
1 parent 9ea3ca1 commit cc4600c

4 files changed

Lines changed: 135 additions & 9 deletions

File tree

crates/shared/src/system/unix/executor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub fn execute_app(
6666
}
6767
None => {
6868
// Has the stop trigger been activated?
69-
if stop.wait_timeout(Duration::from_millis(300)) {
69+
if stop.wait_timeout(Duration::from_millis(300)).is_ok() {
7070
log::info!("Stop trigger activated, killing process");
7171
let _ = child.kill();
7272
let _ = child.wait();

crates/tunnel/src/client/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ where
133133
packet = self.crypt_inbound.read(&self.stop, &mut self.reader, &mut buffer) => {
134134
let (decrypted_data, channel) = packet.context("Failed to read packet from tunnel server")?;
135135
// if decrypted_data is empty, it means the connection was closed
136-
if decrypted_data.is_empty() {
136+
if decrypted_data.is_empty() && !self.stop.is_triggered() {
137137
log::info!("Tunnel server closed the connection");
138138
self.proxy
139139
.connection_closed()

crates/tunnel/src/client/tests.rs

Lines changed: 115 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2727
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2828
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29-
30-
use crate::{crypt::types::SharedSecret, proxy::Command};
29+
use crate::{
30+
crypt::{self, Crypt, types::SharedSecret},
31+
proxy::Command,
32+
};
3133

3234
// Authors: Adolfo Gómez, dkmaster at dkmon dot com
3335
use super::*;
@@ -41,6 +43,8 @@ struct TestContext {
4143
ctrl_rx: flume::Receiver<Command>,
4244
payload_tx: flume::Sender<PayloadWithChannel>,
4345
payload_rx: flume::Receiver<PayloadWithChannel>,
46+
crypt_inbound: Crypt,
47+
crypt_outbound: Crypt,
4448
stop: Trigger,
4549
}
4650

@@ -58,21 +62,26 @@ fn create_client() -> TestContext {
5862

5963
let stop = Trigger::new();
6064

65+
let crypt_inbound = Crypt::new(&secret_in, 0);
66+
let crypt_outbound = Crypt::new(&secret_out, 16);
67+
6168
// Crate a tunnel client with async-everything to ease testing
6269
TestContext {
6370
client: TunnelClient {
6471
reader: client_reader,
6572
writer: client_writer,
6673
tx: client_tx,
6774
rx: client_rx,
68-
crypt_inbound: Crypt::new(&secret_in, 0),
69-
crypt_outbound: Crypt::new(&secret_out, 16),
75+
crypt_inbound: crypt_inbound.clone(),
76+
crypt_outbound: crypt_outbound.clone(),
7077
stop: stop.clone(),
7178
proxy: Handler::new(ctrl_tx.clone()),
7279
},
7380
local,
7481
ctrl_tx,
7582
ctrl_rx,
83+
crypt_inbound,
84+
crypt_outbound,
7685
payload_tx,
7786
payload_rx,
7887
stop,
@@ -118,7 +127,7 @@ async fn check_stop() {
118127
}
119128

120129
#[tokio::test]
121-
async fn check_connection_closed() {
130+
async fn check_remote_connection_closed() {
122131
let TestContext {
123132
client,
124133
local,
@@ -162,7 +171,7 @@ async fn check_connection_closed() {
162171
}
163172

164173
#[tokio::test]
165-
async fn inbound_channel_closed_works_finely() {
174+
async fn inbound_chan_closed_works_finely() {
166175
let TestContext {
167176
client,
168177
ctrl_tx: _ctrl_tx,
@@ -190,6 +199,7 @@ async fn inbound_channel_closed_works_finely() {
190199
});
191200

192201
drop(payload_tx);
202+
// Send something using locak, to ensure data is got
193203

194204
// If not stopped in time, it's a failure, as it means the client did not detect the channel closure
195205
stopped
@@ -203,3 +213,102 @@ async fn inbound_channel_closed_works_finely() {
203213
"Expected no commands to be sent to proxy after channel closure"
204214
);
205215
}
216+
217+
#[tokio::test]
218+
async fn outbound_chan_closed_works_finely() {
219+
let TestContext {
220+
client,
221+
mut local, // We need to keep the cannels alive, event if not used
222+
ctrl_tx: _ctrl_tx,
223+
ctrl_rx,
224+
payload_tx: _payload_tx,
225+
payload_rx,
226+
mut crypt_outbound,
227+
stop,
228+
..
229+
} = create_client();
230+
let stopped = Trigger::new(); // used to signal test completion
231+
tokio::spawn({
232+
let stopped = stopped.clone();
233+
async move {
234+
// Run the client, it should stop when we receive connection closed from server
235+
if client.run(None).await.is_err() {
236+
// Must return err, because chanel is closed
237+
log::info!("Client run failed as expected:");
238+
stopped.trigger(); // Signal that the client has stopped
239+
} else {
240+
log::error!(
241+
"Client run completed successfully, expected failure due to channel closure"
242+
);
243+
}
244+
log::info!("Client run completed");
245+
}
246+
});
247+
248+
drop(payload_rx);
249+
250+
// Sends a valid packet, but as the channel is closed, it should cause the client to stop with an error
251+
crypt_outbound
252+
.write(&stop, &mut local, 1, b"test")
253+
.await
254+
.unwrap();
255+
256+
// If not stopped in time, it's a failure, as it means the client did not detect the channel closure
257+
stopped
258+
.wait_timeout_async(std::time::Duration::from_secs(1))
259+
.await
260+
.unwrap();
261+
262+
// No message on ctrl_rx, ensure
263+
assert!(
264+
ctrl_rx.try_recv().is_err(),
265+
"Expected no commands to be sent to proxy after channel closure"
266+
);
267+
}
268+
269+
#[tokio::test]
270+
async fn sends_data() {
271+
let TestContext {
272+
client,
273+
mut local,
274+
// We need to keep the channels alive, event if not used
275+
ctrl_tx: _ctrl_tx,
276+
ctrl_rx: _ctrl_rx,
277+
payload_tx,
278+
payload_rx: _payload_rx,
279+
mut crypt_outbound,
280+
stop,
281+
..
282+
} = create_client();
283+
tokio::spawn({
284+
let stop = stop.clone();
285+
async move {
286+
// Run the client, it should stop when we receive connection closed from server
287+
if let Err(e) = client.run(None).await {
288+
log::error!("Client run failed: {:?}", e);
289+
} else {
290+
log::info!("Client run completed successfully");
291+
}
292+
log::info!("Client run completed");
293+
stop.trigger(); // Signal that the client has stopped
294+
}
295+
});
296+
297+
// Send something using payload_tx
298+
payload_tx
299+
.send_async(PayloadWithChannel::new(1, b"test"))
300+
.await
301+
.unwrap();
302+
303+
// Read from local and decrypt
304+
let mut buffer = PacketBuffer::new();
305+
let (data, channel_id) = crypt_outbound
306+
.read(&stop, &mut local, &mut buffer)
307+
.await
308+
.unwrap();
309+
310+
assert_eq!(channel_id, 1);
311+
assert_eq!(data, b"test");
312+
313+
stop.trigger(); // Stop the client
314+
}

crates/tunnel/src/crypt/mod.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub mod tunnel;
4444
pub mod types;
4545

4646
pub struct Crypt {
47+
key: types::SharedSecret,
4748
cipher: Aes256Gcm,
4849
seq: u64,
4950
}
@@ -52,7 +53,11 @@ impl Crypt {
5253
pub fn new(key: &types::SharedSecret, seq: u64) -> Self {
5354
log::debug!("Creating Crypt with initial seq: {}", seq);
5455
let cipher = Aes256Gcm::new(key.as_ref().into());
55-
Crypt { cipher, seq }
56+
Crypt {
57+
key: *key,
58+
cipher,
59+
seq,
60+
}
5661
}
5762

5863
/// Increments and returns the internal seq.
@@ -160,6 +165,18 @@ impl Crypt {
160165
}
161166
}
162167

168+
impl Clone for Crypt {
169+
fn clone(&self) -> Self {
170+
log::debug!("Cloning Crypt with seq: {}", self.seq);
171+
let cipher = Aes256Gcm::new(self.key.as_ref().into());
172+
Crypt {
173+
cipher,
174+
key: self.key,
175+
seq: self.seq,
176+
}
177+
}
178+
}
179+
163180
pub fn parse_header(buffer: &[u8]) -> Result<(u64, u16)> {
164181
if buffer.len() < 10 {
165182
return Err(anyhow::anyhow!("buffer too small for header"));

0 commit comments

Comments
 (0)