Save
Some checks failed
Rust Checks / checks (push) Failing after 2m20s

This commit is contained in:
Tipragot 2024-02-11 21:19:42 +01:00
parent 543b6973bb
commit 787dde0bb7
4 changed files with 511 additions and 72 deletions

194
Cargo.lock generated
View file

@ -1796,6 +1796,19 @@ dependencies = [
"syn 2.0.48", "syn 2.0.48",
] ]
[[package]]
name = "env_logger"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
dependencies = [
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]] [[package]]
name = "epaint" name = "epaint"
version = "0.24.1" version = "0.24.1"
@ -1826,16 +1839,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "error-code" name = "error-code"
version = "2.3.1" version = "2.3.1"
@ -2482,6 +2485,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.1.0" version = "1.1.0"
@ -2631,6 +2640,17 @@ dependencies = [
"mach2", "mach2",
] ]
[[package]]
name = "is-terminal"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.12.1" version = "0.12.1"
@ -2810,12 +2830,6 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]] [[package]]
name = "local-ip-address" name = "local-ip-address"
version = "0.5.7" version = "0.5.7"
@ -2982,24 +2996,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "native-tls"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.7.0" version = "0.7.0"
@ -3527,6 +3523,16 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "pretty_env_logger"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c"
dependencies = [
"env_logger",
"log",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "1.3.1" version = "1.3.1"
@ -3688,6 +3694,10 @@ name = "relay-client"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"log", "log",
"mio",
"openssl",
"pretty_env_logger",
"rand",
"tungstenite", "tungstenite",
] ]
@ -3709,6 +3719,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
[[package]]
name = "ring"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "rodio" name = "rodio"
version = "0.17.3" version = "0.17.3"
@ -3744,16 +3768,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustix" name = "rustls"
version = "0.38.31" version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
dependencies = [ dependencies = [
"bitflags 2.4.2", "log",
"errno", "ring",
"libc", "rustls-pki-types",
"linux-raw-sys", "rustls-webpki",
"windows-sys 0.52.0", "subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
dependencies = [
"openssl-probe",
"rustls-pemfile",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-pemfile"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4"
dependencies = [
"base64 0.21.7",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf"
[[package]]
name = "rustls-webpki"
version = "0.102.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
] ]
[[package]] [[package]]
@ -3957,6 +4022,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "spirv" name = "spirv"
version = "0.2.0+1.5.4" version = "0.2.0+1.5.4"
@ -4045,19 +4116,6 @@ dependencies = [
"slotmap", "slotmap",
] ]
[[package]]
name = "tempfile"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"fastrand 2.0.1",
"redox_syscall 0.4.1",
"rustix",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@ -4355,12 +4413,15 @@ dependencies = [
"http 1.0.0", "http 1.0.0",
"httparse", "httparse",
"log", "log",
"native-tls",
"rand", "rand",
"rustls",
"rustls-native-certs",
"rustls-pki-types",
"sha1", "sha1",
"thiserror", "thiserror",
"url", "url",
"utf-8", "utf-8",
"webpki-roots",
] ]
[[package]] [[package]]
@ -4422,6 +4483,12 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.0" version = "2.5.0"
@ -4599,6 +4666,15 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "webpki-roots"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.8" version = "0.1.8"
@ -5136,3 +5212,9 @@ dependencies = [
"quote", "quote",
"syn 2.0.48", "syn 2.0.48",
] ]
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"

View file

@ -13,4 +13,8 @@ workspace = true
[dependencies] [dependencies]
log = "0.4.20" log = "0.4.20"
tungstenite = { version = "0.21.0", features = ["native-tls"] } mio = { version = "0.8.10", features = ["net", "os-poll"] }
openssl = "0.10.63"
pretty_env_logger = "0.5.0"
rand = "0.8.5"
tungstenite = { version = "0.21.0", features = ["rustls", "rustls-native-certs", "rustls-pki-types", "rustls-tls-native-roots", "rustls-tls-webpki-roots"] }

View file

@ -1,23 +1,108 @@
//! A client to use a relay server. //! A client to use a relay server.
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::LinkedList; use std::io::ErrorKind;
use std::net::TcpStream; use std::net::ToSocketAddrs;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{channel, Receiver, Sender};
use log::warn; use log::warn;
use tungstenite::{Message, WebSocket}; use mio::net::TcpStream;
use rand::seq::IteratorRandom;
use tungstenite::handshake::MidHandshake;
use tungstenite::stream::MaybeTlsStream;
use tungstenite::{ClientHandshake, HandshakeError, Message, WebSocket};
/// The state of a [RelayConnection].
#[derive(Debug, PartialEq, Eq)]
pub enum RelayConnectionState {
/// The [RelayConnection] is not connected.
Disconnected,
/// The underlying [TcpStream] is connecting.
Connecting,
/// The [RelayConnection] is making a tls handshake with the relay server.
TlsHandshaking,
/// The [RelayConnection] is making a websocket handshake with the relay
/// server.
WebsocketHandshaking,
/// The [RelayConnection] is connected.
Connected,
}
/// A connection to a relay server.
pub struct RelayConnection { pub struct RelayConnection {
/// The address of the relay server.
server_address: String,
/// The receiver part of the send channel.
///
/// This is used in [RelayConnection::update] to get messages that need to
/// be sent to the relay server.
send_receiver: Receiver<Message>, send_receiver: Receiver<Message>,
/// The sender part of the receive channel.
///
/// This is used in [RelayConnection::send] to store messages that need to
/// be sent to the relay server.
send_sender: Sender<Message>, send_sender: Sender<Message>,
send_buffer: LinkedList<Message>,
/// The receiver part of the receive channel.
///
/// This is used in [RelayConnection::read] to get messages that have been
/// received from the relay server.
receive_receiver: Receiver<(u32, Vec<u8>)>, receive_receiver: Receiver<(u32, Vec<u8>)>,
/// The sender part of the send channel.
///
/// This is used in [RelayConnection::update] to store messages that have
/// been received from the relay server.
receive_sender: Sender<(u32, Vec<u8>)>, receive_sender: Sender<(u32, Vec<u8>)>,
stream: WebSocket<TcpStream>,
/// If the [TcpStream] is not currently connected it will be stored here.
stream: Option<TcpStream>,
/// If the websocket handshake is not complete it will be stored here.
handshake: Option<MidHandshake<ClientHandshake<MaybeTlsStream<TcpStream>>>>,
/// When the websocket is correctly connected it will be stored here.
socket: Option<WebSocket<MaybeTlsStream<TcpStream>>>,
} }
impl RelayConnection { impl RelayConnection {
/// Create a new [RelayConnection].
pub fn new(server_address: String) -> Self {
let (send_sender, send_receiver) = channel();
let (receive_sender, receive_receiver) = channel();
Self {
server_address,
send_receiver,
send_sender,
receive_receiver,
receive_sender,
stream: None,
handshake: None,
socket: None,
}
}
/// Returns the state of the [RelayConnection].
pub fn state(&self) -> RelayConnectionState {
match (
self.stream.is_some(),
self.handshake.is_some(),
self.socket.is_some(),
) {
(false, false, false) => RelayConnectionState::Disconnected,
(true, false, false) => RelayConnectionState::Connecting,
(false, true, false) => RelayConnectionState::WebsocketHandshaking,
(false, false, true) => RelayConnectionState::Connected,
_ => unreachable!(),
}
}
/// Send a message to the target client. /// Send a message to the target client.
pub fn send(&self, target_id: u32, message: Cow<[u8]>) { pub fn send(&self, target_id: u32, message: Cow<[u8]>) {
let mut data = message.into_owned(); let mut data = message.into_owned();
@ -30,18 +115,266 @@ impl RelayConnection {
self.receive_receiver.try_recv().ok() self.receive_receiver.try_recv().ok()
} }
/// Update the [RelayConnection] by sending and receiving messages. /// Update the [RelayConnection].
///
/// This function will connect to the relay server if it's not already
/// connected, and will send and receive messages from the relay server
/// if it's connected.
pub fn update(&mut self) { pub fn update(&mut self) {
while let Ok(message) = self.send_receiver.try_recv() { match (
match self.stream.send(message) { self.stream.take(),
Ok(()) => (), self.handshake.take(),
Err(tungstenite::Error::WriteBufferFull(frame)) => { self.socket.as_mut(),
self.send_buffer.push_back(frame) ) {
} (None, None, None) => {
Err(e) => { // Resolve the relay address list.
warn!("Relay connection closed with error: {}", e); let mut address = self.server_address.clone();
address.push_str(":443");
let address_list = match address.to_socket_addrs() {
Ok(address_list) => address_list,
Err(e) => {
warn!("failed to resolve relay address: {e}");
return;
}
};
// Take a random relay address.
let Some(address) = address_list.choose(&mut rand::thread_rng()) else {
warn!("no relay address available");
return;
};
// Start the connection to the relay.
match TcpStream::connect(address) {
Ok(stream) => self.stream = Some(stream),
Err(e) => warn!("failed to start connection to the relay server: {e}"),
} }
} }
(Some(stream), None, None) => {
// Check if there is an error while connecting.
if let Ok(Some(e)) | Err(e) = stream.take_error() {
warn!("failed to connect to relay: {e}");
return;
}
// Check if the stream is connected.
match stream.peer_addr() {
Ok(_) => {
// Start the websocket handshake.
match tungstenite::client_tls(
format!("wss://{}", self.server_address),
stream,
) {
Ok((socket, _)) => self.socket = Some(socket),
Err(HandshakeError::Interrupted(handshake)) => {
self.handshake = Some(handshake);
}
Err(HandshakeError::Failure(e)) => {
warn!("relay handshake failed: {e}")
}
}
}
Err(ref e) if e.kind() == ErrorKind::NotConnected => {
self.stream = Some(stream);
}
Err(e) => warn!("failed to connect to relay: {e}"),
}
}
(None, Some(handshake), None) => {
// Check if the handshake is complete.
match handshake.handshake() {
Ok((socket, _)) => self.socket = Some(socket),
Err(HandshakeError::Interrupted(unfinished_handshake)) => {
self.handshake = Some(unfinished_handshake);
}
Err(HandshakeError::Failure(e)) => {
warn!("relay websocket handshake failed: {e}")
}
}
}
(None, None, Some(socket)) => {
// Send messages from the send channel to the socket.
while let Ok(message) = self.send_receiver.try_recv() {
match socket.send(message) {
Ok(()) => (),
Err(tungstenite::Error::Io(ref e))
if e.kind() == std::io::ErrorKind::WouldBlock
|| e.kind() == std::io::ErrorKind::Interrupted =>
{
break;
}
Err(e) => {
warn!("relay connection closed with error: {e}");
self.socket = None;
return;
}
}
}
// Receive messages from the socket and send them to the receive channel.
loop {
match socket.read() {
Ok(message) => {
// Check the message length.
let mut data = message.into_data();
if data.len() < 4 {
warn!("received malformed message with length: {}", data.len());
continue;
}
// Extract the sender ID.
let id_start = data.len() - 4;
let sender_id = u32::from_be_bytes(
data[id_start..]
.try_into()
.unwrap_or_else(|_| unreachable!()),
);
data.truncate(id_start);
// Send the message to the receive channel.
self.receive_sender.send((sender_id, data)).ok();
}
Err(tungstenite::Error::Io(ref e))
if e.kind() == std::io::ErrorKind::WouldBlock
|| e.kind() == std::io::ErrorKind::Interrupted =>
{
break;
}
Err(e) => {
warn!("relay connection closed with error: {e}");
self.socket = None;
return;
}
}
}
}
_ => unreachable!(),
} }
} }
// pub fn update(&mut self) {
// // If there's an unconnected stream, wait for it to be connected.
// if let Some(unconnected_stream) = self.unconnected_stream.take() {
// match unconnected_stream.peer_addr() {
// Ok(_) => {
// // Start the handshake.
// match tungstenite::client::client("wss://relay.cocosol.fr",
// unconnected_stream) {
// Ok((socket, _)) => self.socket = Some(socket),
// Err(HandshakeError::Interrupted(unfinished_handshake)) =>
// { self.unfinished_handshake =
// Some(unfinished_handshake); return;
// }
// Err(HandshakeError::Failure(e)) => {
// warn!("relay handshake failed: {}", e);
// return;
// }
// }
// }
// Err(ref e) if e.kind() == std::io::ErrorKind::NotConnected => {
// self.unconnected_stream = Some(unconnected_stream);
// }
// Err(e) => warn!("failed to get peer address: {}", e),
// }
// }
// // If there's an unfinished handshake, try to finish it.
// if let Some(unfinished_handshake) = self.unfinished_handshake.take() {
// match unfinished_handshake.handshake() {
// Ok((socket, _)) => self.socket = Some(socket),
// Err(HandshakeError::Interrupted(unfinished_handshake)) => {
// self.unfinished_handshake = Some(unfinished_handshake)
// }
// Err(HandshakeError::Failure(e)) => warn!("relay handshake failed:
// {}", e), }
// }
// // If there's no socket yet, try to connect.
// let socket = match self.socket {
// Some(ref mut socket) => socket,
// None => {
// // Resolve the relay address list.
// let address_list = match "relay.cocosol.fr:443".to_socket_addrs()
// { Ok(address_list) => address_list,
// Err(e) => {
// warn!("failed to resolve relay address: {}", e);
// return;
// }
// };
// // Take a random relay address.
// let Some(address) = address_list.choose(&mut rand::thread_rng())
// else { warn!("no relay address available");
// return;
// };
// // Create a [TcpStream] connected to the relay.
// self.unconnected_stream = match TcpStream::connect(address) {
// Ok(stream) => Some(stream),
// Err(e) => {
// warn!("failed to connect to relay: {}", e);
// return;
// }
// };
// // Return because the socket is not connected yet.
// return;
// }
// };
// // Send messages from the send channel to the socket.
// while let Ok(message) = self.send_receiver.try_recv() {
// match socket.send(message) {
// Ok(()) => (),
// Err(tungstenite::Error::Io(ref e))
// if e.kind() == std::io::ErrorKind::WouldBlock
// || e.kind() == std::io::ErrorKind::Interrupted =>
// {
// break;
// }
// Err(e) => {
// warn!("relay connection closed with error: {}", e);
// self.socket = None;
// return;
// }
// }
// }
// // Receive messages from the socket and send them to the receive channel.
// loop {
// match socket.read() {
// Ok(message) => {
// // Check the message length.
// let mut data = message.into_data();
// if data.len() < 4 {
// warn!("received malformed message: {}", data.len());
// continue;
// }
// // Extract the sender ID.
// let id_start = data.len() - 4;
// let sender_id = u32::from_be_bytes(
// data[id_start..]
// .try_into()
// .unwrap_or_else(|_| unreachable!()),
// );
// data.truncate(id_start);
// // Send the message to the receive channel.
// self.receive_sender.send((sender_id, data)).ok();
// }
// Err(tungstenite::Error::Io(ref e))
// if e.kind() == std::io::ErrorKind::WouldBlock
// || e.kind() == std::io::ErrorKind::Interrupted =>
// {
// break;
// }
// Err(e) => {
// warn!("relay connection closed with error: {}", e);
// self.socket = None;
// return;
// }
// }
// }
// }
} }

View file

@ -0,0 +1,20 @@
//! TODO
use std::net::TcpStream;
use std::thread;
use std::time::Duration;
use relay_client::RelayConnection;
fn main() {
pretty_env_logger::init();
let mut connection = RelayConnection::new("relay.cocosol.fr".to_string());
loop {
connection.update();
if let Some((sender_id, data)) = connection.read() {
println!("Received message from {sender_id}: {:?}", data);
}
}
}