diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-24 14:48:12 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-24 14:48:12 +0200 |
| commit | 5d6b5049c2a34a3f8abe725d4a871ba84a0dd714 (patch) | |
| tree | 57cb03bf59b3358a96742f455d38c21f4f645396 /src/bin/client/connection.rs | |
| parent | stable and working, both platforms (diff) | |
| download | zagan-bet-5d6b5049c2a34a3f8abe725d4a871ba84a0dd714.tar.gz zagan-bet-5d6b5049c2a34a3f8abe725d4a871ba84a0dd714.zip | |
TLS transport and games framework on client and server
Diffstat (limited to 'src/bin/client/connection.rs')
| -rw-r--r-- | src/bin/client/connection.rs | 136 |
1 files changed, 115 insertions, 21 deletions
diff --git a/src/bin/client/connection.rs b/src/bin/client/connection.rs index b07bf01..c5d506d 100644 --- a/src/bin/client/connection.rs +++ b/src/bin/client/connection.rs | |||
| @@ -1,46 +1,76 @@ | |||
| 1 | use std::io::{BufRead, BufReader, Write}; | 1 | use crate::database; |
| 2 | use native_tls::TlsConnector; | ||
| 3 | use rusqlite::Connection; | ||
| 4 | use std::io::{Read, Write}; | ||
| 2 | use std::net::TcpStream; | 5 | use std::net::TcpStream; |
| 3 | use std::sync::mpsc; | 6 | use std::sync::mpsc; |
| 7 | use std::sync::Arc; | ||
| 8 | use std::sync::Mutex; | ||
| 4 | use std::thread; | 9 | use std::thread; |
| 5 | use std::time::{Duration, Instant}; | 10 | use std::time::{Duration, Instant}; |
| 6 | use rusqlite::Connection; | ||
| 7 | use crate::database; | ||
| 8 | 11 | ||
| 9 | pub fn connect(addr: &str, conn: &Connection) -> Result<(TcpStream, Option<String>), Box<dyn std::error::Error>> { | 12 | static UNSAFE_MODE: Mutex<bool> = Mutex::new(false); |
| 13 | |||
| 14 | pub fn set_unsafe_mode(unsafe_mode: bool) { | ||
| 15 | let mut mode = UNSAFE_MODE.lock().unwrap(); | ||
| 16 | *mode = unsafe_mode; | ||
| 17 | } | ||
| 18 | |||
| 19 | pub fn connect( | ||
| 20 | addr: &str, | ||
| 21 | conn: &Connection, | ||
| 22 | ) -> Result<(TlsStream, Option<String>), Box<dyn std::error::Error>> { | ||
| 10 | let (tx, rx) = mpsc::channel(); | 23 | let (tx, rx) = mpsc::channel(); |
| 11 | let addr_str = addr.to_string(); | 24 | let addr_str = addr.to_string(); |
| 12 | thread::spawn(move || { | 25 | let server_name = addr_str |
| 13 | match TcpStream::connect(&addr_str) { | 26 | .split(':') |
| 14 | Ok(s) => tx.send(Ok(s)).unwrap(), | 27 | .next() |
| 15 | Err(e) => tx.send(Err(e)).unwrap(), | 28 | .unwrap_or("127.0.0.1") |
| 16 | } | 29 | .to_string(); |
| 30 | thread::spawn(move || match TcpStream::connect(&addr_str) { | ||
| 31 | Ok(s) => tx.send(Ok(s)).unwrap(), | ||
| 32 | Err(e) => tx.send(Err(e)).unwrap(), | ||
| 17 | }); | 33 | }); |
| 18 | match rx.recv_timeout(Duration::from_secs(3)) { | 34 | match rx.recv_timeout(Duration::from_secs(3)) { |
| 19 | Ok(Ok(s)) => { | 35 | Ok(Ok(s)) => { |
| 20 | let mut ping_writer = s.try_clone().unwrap(); | 36 | let unsafe_mode = *UNSAFE_MODE.lock().unwrap(); |
| 21 | let mut ping_reader = BufReader::new(s.try_clone().unwrap()); | 37 | |
| 38 | let connector = if unsafe_mode { | ||
| 39 | TlsConnector::builder() | ||
| 40 | .danger_accept_invalid_certs(true) | ||
| 41 | .danger_accept_invalid_hostnames(true) | ||
| 42 | .build()? | ||
| 43 | } else { | ||
| 44 | TlsConnector::builder().build()? | ||
| 45 | }; | ||
| 46 | |||
| 47 | let tls_stream = connector.connect(&server_name, s)?; | ||
| 48 | let mut tls = TlsStream::new(tls_stream); | ||
| 49 | |||
| 22 | let start = Instant::now(); | 50 | let start = Instant::now(); |
| 23 | writeln!(ping_writer, "PING").unwrap(); | 51 | tls.write_line("PING")?; |
| 24 | let mut response = String::new(); | 52 | let mut response = String::new(); |
| 25 | ping_reader.read_line(&mut response).unwrap(); | 53 | tls.read_line(&mut response)?; |
| 26 | let elapsed = start.elapsed(); | 54 | let elapsed = start.elapsed(); |
| 27 | println!("\n\rConnected to {} with ping delay: {:?}", addr, elapsed); | 55 | println!( |
| 28 | 56 | "\n\rConnected to {} with ping delay: {:?} (TLS)", | |
| 29 | // Check for credentials and auto-auth | 57 | addr, elapsed |
| 58 | ); | ||
| 59 | |||
| 30 | if let Some((username, password)) = database::get_credentials(conn, addr) { | 60 | if let Some((username, password)) = database::get_credentials(conn, addr) { |
| 31 | let auth_msg = format!("AUTH {} {}", username, password); | 61 | let auth_msg = format!("AUTH {} {}", username, password); |
| 32 | writeln!(ping_writer, "{}", auth_msg).unwrap(); | 62 | tls.write_line(&auth_msg)?; |
| 33 | let mut auth_response = String::new(); | 63 | let mut auth_response = String::new(); |
| 34 | ping_reader.read_line(&mut auth_response).unwrap(); | 64 | tls.read_line(&mut auth_response)?; |
| 35 | if auth_response.trim().starts_with("OK") { | 65 | if auth_response.trim().starts_with("OK") { |
| 36 | println!("Authenticated as {}", username); | 66 | println!("Authenticated as {}", username); |
| 37 | Ok((s, Some(username))) | 67 | Ok((tls, Some(username))) |
| 38 | } else { | 68 | } else { |
| 39 | println!("Authentication failed: {}", auth_response.trim()); | 69 | println!("Authentication failed: {}", auth_response.trim()); |
| 40 | Ok((s, None)) | 70 | Ok((tls, None)) |
| 41 | } | 71 | } |
| 42 | } else { | 72 | } else { |
| 43 | Ok((s, None)) | 73 | Ok((tls, None)) |
| 44 | } | 74 | } |
| 45 | } | 75 | } |
| 46 | Ok(Err(e)) => Err(Box::new(e)), | 76 | Ok(Err(e)) => Err(Box::new(e)), |
| @@ -48,3 +78,67 @@ pub fn connect(addr: &str, conn: &Connection) -> Result<(TcpStream, Option<Strin | |||
| 48 | Err(_) => Err("Connection attempt failed".into()), | 78 | Err(_) => Err("Connection attempt failed".into()), |
| 49 | } | 79 | } |
| 50 | } | 80 | } |
| 81 | |||
| 82 | pub struct TlsStream { | ||
| 83 | stream: Arc<Mutex<native_tls::TlsStream<TcpStream>>>, | ||
| 84 | } | ||
| 85 | |||
| 86 | unsafe impl Send for TlsStream {} | ||
| 87 | unsafe impl Sync for TlsStream {} | ||
| 88 | |||
| 89 | impl TlsStream { | ||
| 90 | pub fn new(stream: native_tls::TlsStream<TcpStream>) -> Self { | ||
| 91 | Self { | ||
| 92 | stream: Arc::new(Mutex::new(stream)), | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | pub fn read_line(&mut self, buf: &mut String) -> std::io::Result<usize> { | ||
| 97 | let mut byte_buf = [0u8; 1024]; | ||
| 98 | let mut stream = self.stream.lock().unwrap(); | ||
| 99 | loop { | ||
| 100 | let n = stream.read(&mut byte_buf)?; | ||
| 101 | if n == 0 { | ||
| 102 | return Ok(0); | ||
| 103 | } | ||
| 104 | let chunk = std::str::from_utf8(&byte_buf[..n]).unwrap_or(""); | ||
| 105 | buf.push_str(chunk); | ||
| 106 | if buf.contains('\n') { | ||
| 107 | return Ok(n); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | pub fn write_line(&mut self, msg: &str) -> std::io::Result<()> { | ||
| 113 | let mut stream = self.stream.lock().unwrap(); | ||
| 114 | stream.write_all(msg.as_bytes())?; | ||
| 115 | stream.write_all(b"\n")?; | ||
| 116 | stream.flush()?; | ||
| 117 | Ok(()) | ||
| 118 | } | ||
| 119 | |||
| 120 | pub fn try_clone(&self) -> std::io::Result<TlsStream> { | ||
| 121 | Ok(TlsStream { | ||
| 122 | stream: Arc::clone(&self.stream), | ||
| 123 | }) | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | impl Read for TlsStream { | ||
| 128 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { | ||
| 129 | let mut stream = self.stream.lock().unwrap(); | ||
| 130 | stream.read(buf) | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | impl Write for TlsStream { | ||
| 135 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { | ||
| 136 | let mut stream = self.stream.lock().unwrap(); | ||
| 137 | stream.write(buf) | ||
| 138 | } | ||
| 139 | |||
| 140 | fn flush(&mut self) -> std::io::Result<()> { | ||
| 141 | let mut stream = self.stream.lock().unwrap(); | ||
| 142 | stream.flush() | ||
| 143 | } | ||
| 144 | } | ||
