Skip to content

Commit

Permalink
Working on adding TCP support to client
Browse files Browse the repository at this point in the history
  • Loading branch information
manforowicz committed Apr 6, 2024
1 parent 947713b commit 74b7b78
Show file tree
Hide file tree
Showing 12 changed files with 449 additions and 290 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ We stand on the shoulders of giants.
<td>✅</td>
</tr>
<tr>
<td><a href="https://github.com/schollz/croc">croc</a></td>
<td><a href="https://github.com/magic-wormhole/magic-wormhole">magic-wormhole</a></td>
<td>❌</td>
<td>✅</td>
<td>✅</td>
Expand All @@ -43,7 +43,7 @@ We stand on the shoulders of giants.
<td>✅</td>
</tr>
<tr>
<td><a href="https://github.com/magic-wormhole/magic-wormhole">magic-wormhole</a></td>
<td><a href="https://github.com/schollz/croc">croc</a></td>
<td>❌</td>
<td>✅</td>
<td>✅</td>
Expand Down
22 changes: 10 additions & 12 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
# To-Do's
Items to consider implementing. Not all of them are desirable or necessary.
These are just some quick notes that might not make sense.

- Have the hole puncher actively prefer local sockets over public sockets.
But I don't think this matters much since
most NATs don't support hairpin translation, and if they do, I doubt its much slower than a direct connection.
Quick notes on items to consider implementing.
Not all of them are desirable or necessary.

- Deduplicate errors in the error vec the hole puncher can return.
This might give a more helpful error message to the user.

- Give the client and server the option to use plain TCP instead of TLS.
This might be difficult because various inner functions require a get address function.
Maybe I can create a trait that allows for such a function call, and implement this trait
for both raw TCP and TLS? That sounds overly complicated, but maybe it's the only option?
Or potentially just pass an address parameter everywhere??
- Give the client the option to select a server to use.

- Restructure the hole puncher to force keeping connection to server open
during hole-punching. That might please some NATs that lose state when TCP connection is closed.
Expand All @@ -29,4 +21,10 @@ the peer's device is acting as some sort of server.

- Add some sort of end-to-end integration tests.

- Allow sending a simple text string instead of only files.
- Maybe add some versioning to the protocols?

## Low-priority ideas

- Allow sending a simple text string instead of only files.
Though, I don't think this is a common use case, so will only
add if I get requests.
30 changes: 23 additions & 7 deletions gday/src/base32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,33 @@ pub enum Error {
mod tests {
use super::*;

/// Test encoding a message.
#[test]
fn test_general() {
fn test_encode() {
let peer_code = PeerCode {
room_code: 17535328141421925132,
server_id: 4358432574238545432,
shared_secret: 9175435743820743890,
room_code: 1,
server_id: 288,
shared_secret: 1,
};

let message = peer_code.to_str();
assert_eq!(message, "90.1.1.E");
}

/// Test decoding a message with lowercase letters and
/// - o instead of 0
/// - i or l instead of 1
#[test]
fn test_decode() {
let message = "9o.i.l.e";
let received = PeerCode::from_str(message).unwrap();

let peer_code = PeerCode {
room_code: 1,
server_id: 288,
shared_secret: 1,
};

let str: String = peer_code.to_str();
let received = PeerCode::from_str(&str).unwrap();
assert_eq!(peer_code, received);
}

Expand All @@ -184,7 +201,6 @@ mod tests {
};

let str: String = peer_code.to_str();
println!("{str}");
let received = PeerCode::from_str(&str).unwrap();
assert_eq!(peer_code, received);
}
Expand Down
9 changes: 8 additions & 1 deletion gday/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ struct Args {
#[arg(long)]
room: Option<u64>,

/// Use unencrypted TCP instead of TLS. TODO
#[arg(long)]
unencrypted: bool,

/// Use a custom shared secret
#[arg(long)]
secret: Option<u64>,
Expand Down Expand Up @@ -80,7 +84,10 @@ fn run(args: Args) -> Result<(), Box<dyn std::error::Error>> {
// use custom server if the user provided one,
// otherwise pick a random default server
let (mut server_connection, server_id) = if let Some(domain_name) = args.server {
(server_connector::connect_to_domain_name(&domain_name)?, 0)
(
server_connector::connect_to_domain_name(&domain_name, true)?,
0,
)
} else {
server_connector::connect_to_random_server(DEFAULT_SERVERS)?
};
Expand Down
135 changes: 95 additions & 40 deletions gday_encryption/src/helper_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ use chacha20poly1305::aead;
use std::ops::{Deref, DerefMut};

/// Buffer for storing bytes.
/// - Implemented as a heap-allocated array
/// with a left and right cursor defining
/// the in-use portion.
pub struct HelperBuf {
buf: Box<[u8]>,
inner: Box<[u8]>,
l_cursor: usize,
r_cursor: usize,
}
Expand All @@ -12,14 +15,15 @@ impl HelperBuf {
/// Creates a new [`HelperBuf`] with `capacity`.
pub fn with_capacity(capacity: usize) -> Self {
Self {
buf: vec![0; capacity].into_boxed_slice(),
inner: vec![0; capacity].into_boxed_slice(),
l_cursor: 0,
r_cursor: 0,
}
}

/// Removes the first `num_bytes` bytes.
/// Panics if `num_bytes` > `self.len()`
/// Increments the left cursor by `num_bytes` bytes.
/// - Effectively "removes" the first `num_bytes`.
/// - Panics if `num_bytes` > `self.len()`.
pub fn consume(&mut self, num_bytes: usize) {
self.l_cursor += num_bytes;
assert!(self.l_cursor <= self.r_cursor);
Expand All @@ -32,26 +36,28 @@ impl HelperBuf {
}
}

/// Use after putting data to `spare_capacity()`.
pub fn increase_len(&mut self, size: usize) {
self.r_cursor += size;
assert!(self.r_cursor <= self.buf.len());
/// Returns the internal spare capacity after the right cursor.
/// - Put data to the spare capacity, then use [`Self::increase_len()`]
pub fn spare_capacity(&mut self) -> &mut [u8] {
&mut self.inner[self.r_cursor..]
}

/// Returns the internal spare capacity after the stored data.
pub fn spare_capacity(&mut self) -> &mut [u8] {
&mut self.buf[self.r_cursor..]
/// Increment the right cursor by `num_bytes`.
/// - Do this after putting data to [`Self::spare_capacity()`].
pub fn increase_len(&mut self, num_bytes: usize) {
self.r_cursor += num_bytes;
assert!(self.r_cursor <= self.inner.len());
}

/// Moves the stored data to the beginning of the internal buffer.
/// Shifts the stored data to the beginning of the internal buffer.
/// Maximizes `spare_capacity_len()` without changing anything else.
pub fn left_align(&mut self) {
self.buf.copy_within(self.l_cursor..self.r_cursor, 0);
self.inner.copy_within(self.l_cursor..self.r_cursor, 0);
self.r_cursor -= self.l_cursor;
self.l_cursor = 0;
}

/// Returns a mutable view into the part of this
/// Returns a mutable [`aead::Buffer`] view into the part of this
/// buffer starting at index `i`.
pub fn split_off_aead_buf(&mut self, i: usize) -> HelperBufPart {
let start_i = self.l_cursor + i;
Expand All @@ -63,16 +69,21 @@ impl HelperBuf {
}

impl aead::Buffer for HelperBuf {
/// Extends the [`HelperBuf`] with `other`.
/// - Returns an [`aead::Error`] if there's not enough capacity.
fn extend_from_slice(&mut self, other: &[u8]) -> aead::Result<()> {
let new_r_cursor = self.r_cursor + other.len();
if new_r_cursor > self.buf.len() {
if new_r_cursor > self.inner.len() {
return Err(aead::Error);
}
self.buf[self.r_cursor..new_r_cursor].copy_from_slice(other);
self.inner[self.r_cursor..new_r_cursor].copy_from_slice(other);
self.r_cursor = new_r_cursor;
Ok(())
}

/// Shortens the length of [`HelperBuf`] to `len`
/// by cutting off data at the end.
/// - Panics if `len > self.len()`
fn truncate(&mut self, len: usize) {
let new_r_cursor = self.l_cursor + len;
assert!(new_r_cursor <= self.r_cursor);
Expand All @@ -86,13 +97,13 @@ impl Deref for HelperBuf {
type Target = [u8];

fn deref(&self) -> &Self::Target {
&self.buf[self.l_cursor..self.r_cursor]
&self.inner[self.l_cursor..self.r_cursor]
}
}

impl DerefMut for HelperBuf {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buf[self.l_cursor..self.r_cursor]
&mut self.inner[self.l_cursor..self.r_cursor]
}
}

Expand All @@ -108,25 +119,30 @@ impl AsMut<[u8]> for HelperBuf {
}
}

/// A mutable view into the back part of a `HelperBuf`.
/// A mutable view into the back part of a [`HelperBuf`].
pub struct HelperBufPart<'a> {
/// The `HelperBuf` this struct references.
/// The [`HelperBuf`] this struct references.
parent: &'a mut HelperBuf,
/// The index in `parent` where this view begins.
start_i: usize,
}

impl<'a> aead::Buffer for HelperBufPart<'a> {
/// Extends the [`HelperBufPart`] with `other`.
/// - Returns an [`aead::Error`] if there's not enough capacity.
fn extend_from_slice(&mut self, other: &[u8]) -> aead::Result<()> {
let new_r_cursor = self.parent.r_cursor + other.len();
if new_r_cursor > self.parent.buf.len() {
if new_r_cursor > self.parent.inner.len() {
return Err(aead::Error);
}
self.parent.buf[self.parent.r_cursor..new_r_cursor].copy_from_slice(other);
self.parent.inner[self.parent.r_cursor..new_r_cursor].copy_from_slice(other);
self.parent.r_cursor = new_r_cursor;
Ok(())
}

/// Shortens the length of this [`HelperBufPart`] to `len`
/// by cutting off data at the end.
/// - Panics if `len > self.len()`
fn truncate(&mut self, len: usize) {
let new_r_cursor = self.start_i + len;
assert!(new_r_cursor <= self.parent.r_cursor);
Expand All @@ -140,13 +156,13 @@ impl<'a> Deref for HelperBufPart<'a> {
type Target = [u8];

fn deref(&self) -> &Self::Target {
&self.parent.buf[self.start_i..self.parent.r_cursor]
&self.parent.inner[self.start_i..self.parent.r_cursor]
}
}

impl<'a> DerefMut for HelperBufPart<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.parent.buf[self.start_i..self.parent.r_cursor]
&mut self.parent.inner[self.start_i..self.parent.r_cursor]
}
}

Expand All @@ -165,33 +181,72 @@ impl<'a> AsMut<[u8]> for HelperBufPart<'a> {
#[cfg(test)]
mod tests {
use crate::helper_buf::HelperBuf;
use chacha20poly1305::aead::Buffer;
use chacha20poly1305::aead::{self, Buffer};

#[test]
fn test_helper_buf() {
let mut buf = HelperBuf::with_capacity(4);
assert_eq!(buf.buf.len(), 4);
assert!(buf.is_empty());
assert_eq!(buf.spare_capacity().len(), 4);
assert!(buf[..].is_empty());
assert_eq!(buf.spare_capacity(), [0, 0, 0, 0]);
assert_eq!(*buf.inner, [0, 0, 0, 0]);

buf.extend_from_slice(&[1, 2, 3]).unwrap();
assert_eq!(buf[..], [1, 2, 3][..]);
assert_eq!(*buf.buf, [1, 2, 3, 0]);
assert_eq!(buf.spare_capacity().len(), 1);
assert_eq!(*buf, [1, 2, 3]);
assert_eq!(buf.spare_capacity(), [0]);
assert_eq!(*buf.inner, [1, 2, 3, 0]);

buf.consume(1);
assert_eq!(buf[..], [2, 3][..]);
assert_eq!(*buf.buf, [1, 2, 3, 0]);
assert_eq!(buf.spare_capacity().len(), 1);
assert_eq!(*buf, [2, 3]);
assert_eq!(buf.spare_capacity(), [0]);
assert_eq!(*buf.inner, [1, 2, 3, 0]);

buf[0] = 7;
assert_eq!(*buf, [7, 3]);
assert_eq!(buf.spare_capacity(), [0]);
assert_eq!(*buf.inner, [1, 7, 3, 0]);

buf.left_align();
assert_eq!(buf[..], [2, 3][..]);
assert_eq!(*buf.buf, [2, 3, 3, 0]);
assert_eq!(buf.spare_capacity().len(), 2);
assert_eq!(*buf, [7, 3]);
assert_eq!(buf.spare_capacity(), [3, 0]);
assert_eq!(*buf.inner, [7, 3, 3, 0]);

buf.consume(1);
assert_eq!(buf[..], [3][..]);
assert_eq!(*buf.buf, [2, 3, 3, 0]);
assert_eq!(buf.spare_capacity().len(), 2);
buf.spare_capacity()[0] = 5;
buf.increase_len(1);
assert_eq!(*buf, [7, 3, 5]);
assert_eq!(buf.spare_capacity(), [0]);
assert_eq!(*buf.inner, [7, 3, 5, 0]);

// Trying to extend by slice longer than spare capacity
// results in an error
assert_eq!(buf.extend_from_slice(&[2, 2, 2, 2]), Err(aead::Error));

buf.truncate(1);
assert_eq!(*buf, [7]);
assert_eq!(buf.spare_capacity(), [3, 5, 0]);
assert_eq!(*buf.inner, [7, 3, 5, 0]);
}

#[test]
fn test_helper_buf_part() {
let mut buf = HelperBuf::with_capacity(4);

buf.extend_from_slice(&[1, 2, 3]).unwrap();
assert_eq!(*buf, [1, 2, 3]);
let mut part = buf.split_off_aead_buf(1);
assert_eq!(*part, [2, 3]);

part[0] = 5;
assert_eq!(*part, [5, 3]);

part.extend_from_slice(&[6]).unwrap();
assert_eq!(*part, [5, 3, 6]);

assert_eq!(part.extend_from_slice(&[0]), Err(aead::Error));

part.truncate(1);
assert_eq!(*part, [5]);

assert_eq!(*buf, [1, 5]);
}
}
Loading

0 comments on commit 74b7b78

Please sign in to comment.