use super::{
error::Error,
protocol::{Link, Protocol, ProtocolV1},
AvailableDeviceTransport, ProtoMessage, Transport,
};
use crate::{AvailableDevice, Model};
use std::{fmt, net::UdpSocket, result::Result, time::Duration};
mod constants {
pub(crate) const DEFAULT_HOST: &str = "127.0.0.1";
pub(crate) const DEFAULT_PORT: &str = "21324";
pub(crate) const DEFAULT_DEBUG_PORT: &str = "21325";
pub(crate) const LOCAL_LISTENER: &str = "127.0.0.1:0";
}
use constants::{DEFAULT_DEBUG_PORT, DEFAULT_HOST, DEFAULT_PORT, LOCAL_LISTENER};
const CHUNK_SIZE: usize = 64;
const READ_TIMEOUT_MS: u64 = 100000;
const WRITE_TIMEOUT_MS: u64 = 100000;
#[derive(Debug)]
pub struct AvailableUdpTransport {
pub host: String,
pub port: String,
}
impl fmt::Display for AvailableUdpTransport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "udp:{}:{}", self.host, self.port)
}
}
struct UdpLink {
socket: UdpSocket,
device: (String, String),
}
impl Link for UdpLink {
fn write_chunk(&mut self, chunk: Vec<u8>) -> Result<(), Error> {
debug_assert_eq!(CHUNK_SIZE, chunk.len());
let timeout = Duration::from_millis(WRITE_TIMEOUT_MS);
self.socket.set_write_timeout(Some(timeout))?;
if let Err(e) = self.socket.send(&chunk) {
return Err(e.into())
}
Ok(())
}
fn read_chunk(&mut self) -> Result<Vec<u8>, Error> {
let mut chunk = vec![0; CHUNK_SIZE];
let timeout = Duration::from_millis(READ_TIMEOUT_MS);
self.socket.set_read_timeout(Some(timeout))?;
let n = self.socket.recv(&mut chunk)?;
if n == CHUNK_SIZE {
Ok(chunk)
} else {
Err(Error::DeviceReadTimeout)
}
}
}
impl UdpLink {
fn open(path: &str) -> Result<UdpLink, Error> {
let mut parts = path.split(':');
let link = Self {
socket: UdpSocket::bind(LOCAL_LISTENER)?,
device: (
parts.next().expect("Incorrect Path").to_owned(),
parts.next().expect("Incorrect Path").to_owned(),
),
};
link.socket.connect(path)?;
Ok(link)
}
fn ping(&self) -> Result<bool, Error> {
let mut resp = [0; CHUNK_SIZE];
self.socket.send("PINGPING".as_bytes())?;
let size = self.socket.recv(&mut resp)?;
Ok(&resp[..size] == "PONGPONG".as_bytes())
}
}
pub struct UdpTransport {
protocol: ProtocolV1<UdpLink>,
}
impl UdpTransport {
pub fn find_devices(debug: bool, path: Option<&str>) -> Result<Vec<AvailableDevice>, Error> {
let mut devices = Vec::new();
let mut dest = String::new();
match path {
Some(p) => dest = p.to_owned(),
None => {
dest.push_str(DEFAULT_HOST);
dest.push(':');
dest.push_str(if debug { DEFAULT_DEBUG_PORT } else { DEFAULT_PORT });
}
};
let link = UdpLink::open(&dest)?;
if link.ping()? {
devices.push(AvailableDevice {
model: Model::TrezorEmulator,
debug,
transport: AvailableDeviceTransport::Udp(AvailableUdpTransport {
host: link.device.0,
port: link.device.1,
}),
});
}
Ok(devices)
}
pub fn connect(device: &AvailableDevice) -> Result<Box<dyn Transport>, Error> {
let transport = match device.transport {
AvailableDeviceTransport::Udp(ref t) => t,
_ => panic!("passed wrong AvailableDevice in UdpTransport::connect"),
};
let mut path = String::new();
path.push_str(&transport.host);
path.push(':');
path.push_str(&transport.port);
let link = UdpLink::open(&path)?;
Ok(Box::new(UdpTransport { protocol: ProtocolV1 { link } }))
}
}
impl super::Transport for UdpTransport {
fn session_begin(&mut self) -> Result<(), Error> {
self.protocol.session_begin()
}
fn session_end(&mut self) -> Result<(), Error> {
self.protocol.session_end()
}
fn write_message(&mut self, message: ProtoMessage) -> Result<(), Error> {
self.protocol.write(message)
}
fn read_message(&mut self) -> Result<ProtoMessage, Error> {
self.protocol.read()
}
}