From 9b5c37ceb34fb961a4a16a310da6d6d21113929e Mon Sep 17 00:00:00 2001 From: TudbuT Date: Sun, 1 Oct 2023 16:45:40 +0200 Subject: [PATCH] initial modem commit --- Cargo.lock | 97 ++++++++++++++++++++------- Cargo.toml | 1 + modem-init.txt | 28 ++++++++ src/client.rs | 86 ++++++++++++++++++++---- src/lib.rs | 3 + src/main.rs | 25 +++---- src/server.rs | 12 ++-- src/server_connection.rs | 139 +++++++++++++++++++++++++++++++++++++++ src/socket_adapter.rs | 8 +-- 9 files changed, 344 insertions(+), 55 deletions(-) create mode 100644 modem-init.txt create mode 100644 src/server_connection.rs diff --git a/Cargo.lock b/Cargo.lock index ed0f217..0ff9e09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,23 +10,37 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "enum-ordinalize" -version = "3.1.12" +version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bb1df8b45ecb7ffa78dca1c17a438fb193eb083db0b1b494d2a61bcb5096a" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ "num-bigint", "num-traits", "proc-macro2", "quote", - "rustc_version", "syn", ] [[package]] -name = "num-bigint" -version = "0.4.3" +name = "ioctl-rs" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -45,27 +59,27 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -75,28 +89,56 @@ name = "revpfw3" version = "0.3.1" dependencies = [ "enum-ordinalize", + "serial", ] [[package]] -name = "rustc_version" +name = "serial" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86" dependencies = [ - "semver", + "serial-core", + "serial-unix", + "serial-windows", ] [[package]] -name = "semver" -version = "1.0.16" +name = "serial-core" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581" +dependencies = [ + "libc", +] + +[[package]] +name = "serial-unix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7" +dependencies = [ + "ioctl-rs", + "libc", + "serial-core", + "termios", +] + +[[package]] +name = "serial-windows" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162" +dependencies = [ + "libc", + "serial-core", +] [[package]] name = "syn" -version = "1.0.107" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -104,7 +146,16 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.6" +name = "termios" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" +dependencies = [ + "libc", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml index a2c3dff..e1029b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ edition = "2021" [dependencies] enum-ordinalize = "3.1" +serial = "0.4" diff --git a/modem-init.txt b/modem-init.txt new file mode 100644 index 0000000..703d505 --- /dev/null +++ b/modem-init.txt @@ -0,0 +1,28 @@ + +AT + + + + + + + + + + + + + + + + +AT+CFUN? +AT+CPIN? +AT+CIPMODE=1 + +AT+CSTT="internet.telekom","","" +AT+CIICR + + +AT+CIFSR +AT+CIPSTART=TCP,"$IP",$PORT \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index d5ee3bd..3f1a54e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,27 +1,89 @@ use std::{ collections::HashMap, - io::Read, - io::Write, + io::{Read, Write}, net::{Shutdown, TcpStream}, thread, time::{Duration, SystemTime}, vec, }; -use crate::{io_sync, PacketType, SocketAdapter}; +use serial::SerialPort; -pub fn client(ip: &str, port: u16, dest_ip: &str, dest_port: u16, key: &str, sleep_delay_ms: u64) { +use crate::{io_sync, Connection, PacketType, SocketAdapter}; + +pub struct ClientParams<'a> { + pub server_ip: &'a str, + pub server_port: u16, + pub dest_ip: &'a str, + pub dest_port: u16, + pub key: &'a str, + pub sleep_delay_ms: u64, + pub modem_port: Option<&'a str>, + pub modem_baud: Option, + pub modem_init: Option<&'a str>, +} + +fn connect(params: &ClientParams) -> Connection { + if let Some(modem_port) = params.modem_port { + let mut serial = serial::open(modem_port).unwrap(); + serial + .configure(&serial::PortSettings { + baud_rate: serial::BaudRate::from_speed( + params.modem_baud.unwrap_or(115200) as usize + ), + char_size: serial::CharSize::Bits8, + parity: serial::Parity::ParityNone, + stop_bits: serial::StopBits::Stop1, + flow_control: serial::FlowControl::FlowNone, + }) + .unwrap(); + if let Some(modem_init) = params.modem_init { + serial.set_timeout(Duration::from_millis(200)).unwrap(); + for line in modem_init.lines() { + let line = line + .replace("$IP", ¶ms.server_ip.to_string()) + .replace("$PORT", ¶ms.server_port.to_string()); + println!("> {line}"); + serial.write_all((line + "\r\n").as_bytes()).unwrap(); + let mut s = Vec::new(); + let _ = serial.read_to_end(&mut s).is_ok(); + if !s.is_empty() { + println!( + "< {}", + String::from_utf8(s).unwrap().replace("\n", "\n< ").trim() + ); + } + thread::sleep(Duration::from_millis(300)); + } + serial.set_timeout(Duration::from_millis(3000)).unwrap(); + let mut s = Vec::new(); + let _ = serial.read_to_end(&mut s).is_ok(); + if !s.is_empty() { + println!( + "< {}", + String::from_utf8(s).unwrap().replace("\n", "\n< ").trim() + ); + } + } + serial.set_timeout(Duration::from_millis(10000)).unwrap(); + return Connection::new_serial(serial); + } + Connection::new_tcp(TcpStream::connect((params.server_ip, params.server_port)).unwrap()) +} + +pub fn client(params: ClientParams) { let mut buf1 = [0u8; 1]; let mut buf4 = [0u8; 4]; let mut buf8 = [0u8; 8]; let mut buf16 = [0u8; 16]; let mut buf = [0; 1024]; - let mut tcp = TcpStream::connect((ip, port)).unwrap(); + let mut tcp = connect(¶ms); println!("Syncing..."); tcp.write_all(&[b'R', b'P', b'F', 30]).unwrap(); println!("Authenticating..."); - tcp.write_all(&(key.len() as u32).to_be_bytes()).unwrap(); - tcp.write_all(key.as_bytes()).unwrap(); + tcp.write_all(&(params.key.len() as u32).to_be_bytes()) + .unwrap(); + tcp.write_all(params.key.as_bytes()).unwrap(); println!("Syncing..."); tcp.read_exact(&mut buf4).unwrap(); @@ -77,7 +139,7 @@ pub fn client(ip: &str, port: u16, dest_ip: &str, dest_port: u16, key: &str, sle .unwrap(); tcp.write(&i.to_be_bytes()).unwrap(); if let Some(x) = sockets.remove(&i) { - let _ = x.internal.shutdown(Shutdown::Both); + let _ = x.internal.close(); } } @@ -87,7 +149,7 @@ pub fn client(ip: &str, port: u16, dest_ip: &str, dest_port: u16, key: &str, sle .is_none() { if !did_anything { - thread::sleep(Duration::from_millis(sleep_delay_ms)); + thread::sleep(Duration::from_millis(params.sleep_delay_ms)); } continue; } @@ -97,7 +159,9 @@ pub fn client(ip: &str, port: u16, dest_ip: &str, dest_port: u16, key: &str, sle tcp.set_nonblocking(false); match pt { PacketType::NewClient => { - let mut tcp = SocketAdapter::new(TcpStream::connect((dest_ip, dest_port)).unwrap()); + let mut tcp = SocketAdapter::new(Connection::new_tcp( + TcpStream::connect((params.dest_ip, params.dest_port)).unwrap(), + )); tcp.set_nonblocking(true); sockets.insert((id, id += 1).0, tcp); } @@ -105,7 +169,7 @@ pub fn client(ip: &str, port: u16, dest_ip: &str, dest_port: u16, key: &str, sle PacketType::CloseClient => { tcp.internal.read_exact(&mut buf8).unwrap(); if let Some(x) = sockets.remove(&u64::from_be_bytes(buf8)) { - let _ = x.internal.shutdown(Shutdown::Both); + let _ = x.internal.close(); } } diff --git a/src/lib.rs b/src/lib.rs index 18b3561..c26ba90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ mod client; mod packet; mod server; +mod server_connection; mod socket_adapter; use std::io::{Error, ErrorKind}; @@ -8,12 +9,14 @@ use std::io::{Error, ErrorKind}; pub use client::*; pub(crate) use packet::*; pub use server::*; +pub(crate) use server_connection::*; pub(crate) use socket_adapter::*; pub(crate) fn io_sync(result: Result) -> Result, Error> { match result { Ok(x) => Ok(Some(x)), Err(x) if x.kind() == ErrorKind::WouldBlock => Ok(None), + Err(x) if x.kind() == ErrorKind::TimedOut => Ok(None), Err(x) => Err(x), } } diff --git a/src/main.rs b/src/main.rs index bb013db..97dd997 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,25 @@ use std::env; -use revpfw3::{client, server}; +use revpfw3::{client, server, ClientParams}; fn main() { let args: Vec<_> = env::args().skip(1).collect(); - if (6..=7).contains(&args.len()) && args[0] == "client" { - client( - &args[1], - args[2].parse().unwrap(), - &args[3], - args[4].parse().unwrap(), - &args[5], - if args.len() == 7 { + if (6..=10).contains(&args.len()) && args[0] == "client" { + client(ClientParams { + server_ip: &args[1], + server_port: args[2].parse().unwrap(), + dest_ip: &args[3], + dest_port: args[4].parse().unwrap(), + key: &args[5], + sleep_delay_ms: if args.len() == 7 { args[6].parse().unwrap() } else { 1 }, - ); + modem_port: args.get(7).map(|x| x.as_str()), + modem_baud: args.get(8).map(|x| x.parse().unwrap()), + modem_init: args.get(9).map(|x| x.as_str()), + }); } if (3..=4).contains(&args.len()) && args[0] == "server" { server( @@ -31,5 +34,5 @@ fn main() { } eprintln!("Usage: \n\ \x20 revpfw3 server []\n\ - \x20 revpfw3 client []"); + \x20 revpfw3 client [ [ ]]"); } diff --git a/src/server.rs b/src/server.rs index c35bc09..a6d3d07 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,7 +8,7 @@ use std::{ vec, }; -use crate::{io_sync, PacketType, SocketAdapter}; +use crate::{io_sync, Connection, PacketType, SocketAdapter}; pub fn server(port: u16, key: &str, sleep_delay_ms: u64) { let mut buf1 = [0u8; 1]; @@ -42,7 +42,7 @@ pub fn server(port: u16, key: &str, sleep_delay_ms: u64) { tcpl.set_nonblocking(true).unwrap(); - let mut tcp = SocketAdapter::new(tcp); + let mut tcp = SocketAdapter::new(Connection::new_tcp(tcp)); tcp.set_nonblocking(true); let mut sockets: HashMap = HashMap::new(); let mut id = 0; @@ -51,7 +51,7 @@ pub fn server(port: u16, key: &str, sleep_delay_ms: u64) { loop { let mut did_anything = false; - if last_keep_alive_sent.elapsed().unwrap().as_secs() >= 20 { + if last_keep_alive_sent.elapsed().unwrap().as_secs() >= 10 { last_keep_alive_sent = SystemTime::now(); tcp.write(&[PacketType::KeepAlive.ordinal() as u8]).unwrap(); } @@ -60,7 +60,7 @@ pub fn server(port: u16, key: &str, sleep_delay_ms: u64) { } if let Ok(new) = tcpl.accept() { - let mut new = SocketAdapter::new(new.0); + let mut new = SocketAdapter::new(Connection::new_tcp(new.0)); new.set_nonblocking(true); sockets.insert((id, id += 1).0, new); tcp.write(&[PacketType::NewClient.ordinal() as u8]).unwrap(); @@ -99,7 +99,7 @@ pub fn server(port: u16, key: &str, sleep_delay_ms: u64) { .unwrap(); tcp.write(&i.to_be_bytes()).unwrap(); if let Some(x) = sockets.remove(&i) { - let _ = x.internal.shutdown(Shutdown::Both); + let _ = x.internal.close(); } } @@ -123,7 +123,7 @@ pub fn server(port: u16, key: &str, sleep_delay_ms: u64) { PacketType::CloseClient => { tcp.internal.read_exact(&mut buf8).unwrap(); if let Some(x) = sockets.remove(&u64::from_be_bytes(buf8)) { - let _ = x.internal.shutdown(Shutdown::Both); + let _ = x.internal.close(); } } diff --git a/src/server_connection.rs b/src/server_connection.rs new file mode 100644 index 0000000..51f2dfd --- /dev/null +++ b/src/server_connection.rs @@ -0,0 +1,139 @@ +use std::{ + io::{self, ErrorKind, Read, Write}, + net::{Shutdown, TcpStream}, + ptr::NonNull, + time::Duration, +}; + +use serial::SerialPort; + +trait ReadWrite: Write + Read + 'static {} +impl ReadWrite for T where T: Write + Read + 'static {} + +pub struct Connection { + readwrite: Box, + data: NonNull<()>, + set_nonblocking_thunk: fn(NonNull<()>, bool) -> io::Result<()>, + close_thunk: fn(NonNull<()>) -> io::Result<()>, + is_nb: bool, +} + +impl Write for Connection { + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.as_write().write_vectored(bufs) + } + + fn write_all(&mut self, mut buf: &[u8]) -> io::Result<()> { + self.as_write().write_all(buf) + } + + fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { + self.as_write().write_fmt(fmt) + } + + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } + + fn write(&mut self, buf: &[u8]) -> io::Result { + self.as_write().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.as_write().flush() + } +} + +impl Read for Connection { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.as_read().read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.as_read().read_vectored(bufs) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.as_read().read_to_end(buf) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.as_read().read_to_string(buf) + } + + fn read_exact(&mut self, mut buf: &mut [u8]) -> io::Result<()> { + while !buf.is_empty() { + match self.read(buf) { + Ok(0) if self.is_nb => { + return Err(io::Error::new(ErrorKind::WouldBlock, "would block")) + } + Ok(0) => (), + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => (), + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new( + ErrorKind::UnexpectedEof, + "failed to fill whole buffer", + )) + } else { + Ok(()) + } + } +} + +impl Connection { + pub fn new_tcp(stream: TcpStream) -> Self { + let mut stream = Box::new(stream); + Connection { + data: NonNull::from(stream.as_mut()).cast(), + readwrite: stream, + set_nonblocking_thunk: |data, nb| unsafe { + data.cast::().as_ref().set_nonblocking(nb) + }, + close_thunk: |data| unsafe { + data.cast::().as_ref().shutdown(Shutdown::Both) + }, + is_nb: false, + } + } + pub fn new_serial(serial: T) -> Self { + let mut serial = Box::new(serial); + Connection { + data: NonNull::from(serial.as_mut()).cast(), + readwrite: serial, + set_nonblocking_thunk: |data, nb| unsafe { + data.cast::() + .as_mut() + .set_timeout(Duration::from_millis(if nb { 1 } else { 10000 })) + .map_err(|_| { + io::Error::new(io::ErrorKind::ConnectionAborted, "serial port went down") + }) + }, + // no need to close this. + close_thunk: |_data| Ok(()), + is_nb: false, + } + } + fn as_read(&mut self) -> &mut (dyn Read) { + &mut self.readwrite + } + fn as_write(&mut self) -> &mut (dyn Write) { + &mut self.readwrite + } + pub fn set_nonblocking(&mut self, nonblocking: bool) -> io::Result<()> { + self.is_nb = nonblocking; + (self.set_nonblocking_thunk)(self.data, nonblocking) + } + pub fn close(&self) -> io::Result<()> { + (self.close_thunk)(self.data) + } +} diff --git a/src/socket_adapter.rs b/src/socket_adapter.rs index af66502..d5b6993 100644 --- a/src/socket_adapter.rs +++ b/src/socket_adapter.rs @@ -5,7 +5,7 @@ use std::{ time::SystemTime, }; -use crate::io_sync; +use crate::{io_sync, Connection}; #[derive(Clone, Copy)] enum Broken { @@ -23,7 +23,7 @@ impl From for Error { } pub(crate) struct SocketAdapter { - pub(crate) internal: TcpStream, + pub(crate) internal: Connection, written: usize, to_write: usize, write: [u8; 65536], @@ -34,9 +34,9 @@ pub(crate) struct SocketAdapter { } impl SocketAdapter { - pub fn new(tcp: TcpStream) -> SocketAdapter { + pub fn new(connection: Connection) -> SocketAdapter { Self { - internal: tcp, + internal: connection, written: 0, to_write: 0, write: [0u8; 65536],