use std::fmt;
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::time::{Duration, Instant};
use crate::command::{
self, CommandResponse, ConnectOptions, Request, RequestKind, ResponseKind, WriteTier,
};
use crate::error::Error as NatNetError;
use crate::message::{MessageHeader, NAT_FRAMEOFDATA, NAT_MODELDEF, NAT_RESPONSE, NAT_SERVERINFO};
use crate::serverinfo::ServerInfo;
use crate::version::Version;
pub const MAX_COMMAND_DATAGRAM: usize = 65_535;
pub const MAX_IPV4_UDP_PAYLOAD: usize = 65_507;
pub const MIN_DATA_BUFFER: usize = MAX_IPV4_UDP_PAYLOAD;
#[cfg(windows)]
const WSAEMSGSIZE: i32 = 10_040;
pub use crate::message::NAT_UNRECOGNIZED_REQUEST;
#[derive(Debug)]
pub struct DataClient {
socket: UdpSocket,
}
impl DataClient {
pub fn bind_multicast(interface: Ipv4Addr, group: Ipv4Addr, port: u16) -> io::Result<Self> {
let socket = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port))?;
socket.join_multicast_v4(&group, &interface)?;
Ok(Self { socket })
}
pub fn bind_unicast(interface: Ipv4Addr, port: u16) -> io::Result<Self> {
let socket = UdpSocket::bind(SocketAddrV4::new(interface, port))?;
Ok(Self { socket })
}
#[must_use]
pub fn from_socket(socket: UdpSocket) -> Self {
Self { socket }
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
self.socket.set_nonblocking(nonblocking)
}
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
self.socket.set_read_timeout(timeout)
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.socket.local_addr()
}
pub fn recv(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
let (received, source) = match self.socket.recv_from(buf) {
Ok(received) => received,
#[cfg(windows)]
Err(error) if error.raw_os_error() == Some(WSAEMSGSIZE) => {
return Err(invalid_data(format_args!(
"datagram was truncated to the {}-byte receive buffer; use a buffer of at \
least MIN_DATA_BUFFER ({MIN_DATA_BUFFER}) bytes",
buf.len()
)));
}
Err(error) => return Err(error),
};
if received == buf.len() && buf.len() < MIN_DATA_BUFFER {
return Err(invalid_data(format_args!(
"datagram from {source} filled the {received}-byte receive buffer and may be \
truncated; use a buffer of at least MIN_DATA_BUFFER ({MIN_DATA_BUFFER}) bytes"
)));
}
Ok((received, source))
}
}
#[derive(Debug)]
pub struct RawCommandClient {
socket: UdpSocket,
server: SocketAddr,
}
impl RawCommandClient {
pub fn bind(interface: Ipv4Addr, server: SocketAddrV4) -> io::Result<Self> {
let socket = UdpSocket::bind(SocketAddrV4::new(interface, 0))?;
Ok(Self {
socket,
server: SocketAddr::V4(server),
})
}
#[must_use]
pub fn server(&self) -> SocketAddr {
self.server
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.socket.local_addr()
}
pub fn send(&self, datagram: &[u8]) -> io::Result<usize> {
self.socket.send_to(datagram, self.server)
}
pub fn send_to(&self, datagram: &[u8], destination: SocketAddr) -> io::Result<usize> {
self.socket.send_to(datagram, destination)
}
pub fn recv(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.socket.recv_from(buf)
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
self.socket.set_nonblocking(nonblocking)
}
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
self.socket.set_read_timeout(timeout)
}
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
self.socket.set_write_timeout(timeout)
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
self.socket.read_timeout()
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
self.socket.write_timeout()
}
}
#[derive(Debug)]
pub struct CommandClient {
socket: UdpSocket,
server: SocketAddr,
}
impl CommandClient {
pub fn bind(interface: Ipv4Addr, server: SocketAddrV4) -> io::Result<Self> {
let socket = UdpSocket::bind(SocketAddrV4::new(interface, 0))?;
let server = SocketAddr::V4(server);
Ok(Self { socket, server })
}
#[must_use]
pub fn server(&self) -> SocketAddr {
self.server
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.socket.local_addr()
}
#[must_use]
pub fn into_raw(self) -> RawCommandClient {
RawCommandClient {
socket: self.socket,
server: self.server,
}
}
pub fn exchange(&mut self, request: &Request, timeout: Duration) -> io::Result<Vec<u8>> {
if request.kind() == RequestKind::SetProperty && request.tier() == Some(WriteTier::Tier1) {
return Err(invalid_input(
"Tier-1 SetProperty must run through execute_tier1",
));
}
self.exchange_inner(request, timeout)
}
pub fn connect_handshake(&mut self, timeout: Duration) -> Result<ServerInfo, HandshakeError> {
let request = command::connect_request().map_err(HandshakeError::Command)?;
self.handshake(&request, timeout, |info| info.protocol_version)
}
pub fn connect_handshake_with(
&mut self,
options: ConnectOptions,
timeout: Duration,
) -> Result<ServerInfo, HandshakeError> {
let request = command::connect_request_with(options).map_err(HandshakeError::Command)?;
self.handshake(&request, timeout, |info| options.stream_version(info))
}
fn handshake(
&mut self,
request: &Request,
timeout: Duration,
layout: impl FnOnce(&ServerInfo) -> Version,
) -> Result<ServerInfo, HandshakeError> {
let datagram = self
.exchange(request, timeout)
.map_err(HandshakeError::Io)?;
let info = ServerInfo::parse(&datagram).map_err(HandshakeError::Decode)?;
if let Err(source) = layout(&info).validate() {
return Err(HandshakeError::UnsupportedVersion { info, source });
}
Ok(info)
}
fn exchange_inner(&mut self, request: &Request, timeout: Duration) -> io::Result<Vec<u8>> {
if timeout.is_zero() {
return Err(invalid_input("command timeout must be nonzero"));
}
let deadline = Instant::now()
.checked_add(timeout)
.ok_or_else(|| invalid_input("command timeout overflows the monotonic clock"))?;
if request.as_bytes().len() > MAX_IPV4_UDP_PAYLOAD {
return Err(invalid_input(format_args!(
"command datagram length {} exceeds IPv4 UDP limit {MAX_IPV4_UDP_PAYLOAD}",
request.as_bytes().len()
)));
}
let previous_read = self.socket.read_timeout()?;
let previous_write = self.socket.write_timeout()?;
let result = self.exchange_with_deadline(request, deadline);
let restore_read = self.socket.set_read_timeout(previous_read);
let restore_write = self.socket.set_write_timeout(previous_write);
match (restore_read, restore_write) {
(Err(error), _) | (_, Err(error)) => Err(error),
(Ok(()), Ok(())) => result,
}
}
fn exchange_with_deadline(&self, request: &Request, deadline: Instant) -> io::Result<Vec<u8>> {
let mut buffer = vec![0u8; MAX_COMMAND_DATAGRAM].into_boxed_slice();
self.drain_queued(&mut buffer, deadline)?;
let write_remaining = remaining(deadline)?;
self.socket.set_write_timeout(Some(write_remaining))?;
self.socket.send_to(request.as_bytes(), self.server)?;
let expected_id = expected_message_id(request.response_kind());
loop {
let remaining = remaining(deadline)?;
self.socket.set_read_timeout(Some(remaining))?;
let (received, source) = match self.socket.recv_from(&mut buffer) {
Ok(received) => received,
Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
"command deadline elapsed",
));
}
Err(error) => return Err(error),
};
if source != self.server {
continue;
}
if received < 2 {
continue;
}
let found = u16::from_le_bytes([buffer[0], buffer[1]]);
if found == NAT_UNRECOGNIZED_REQUEST {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"server replied NAT_UNRECOGNIZED_REQUEST",
));
}
if found != expected_id {
continue;
}
MessageHeader::parse(&buffer[..received])
.map_err(|error| invalid_data(error.to_string()))?;
return Ok(buffer[..received].to_vec());
}
}
fn drain_queued(&self, buffer: &mut [u8], deadline: Instant) -> io::Result<()> {
self.socket.set_nonblocking(true)?;
let drained = loop {
if let Err(error) = remaining(deadline) {
break Err(error);
}
match self.socket.recv_from(buffer) {
Ok(_) => {}
Err(error) if error.kind() == io::ErrorKind::WouldBlock => break Ok(()),
Err(error)
if matches!(
error.kind(),
io::ErrorKind::Interrupted | io::ErrorKind::ConnectionReset
) => {}
Err(error) => break Err(error),
}
};
let restored = self.socket.set_nonblocking(false);
drained?;
restored
}
}
fn expected_message_id(response: ResponseKind) -> u16 {
match response {
ResponseKind::ServerInfo => NAT_SERVERINFO,
ResponseKind::ModelDefinition => NAT_MODELDEF,
ResponseKind::FrameOfData => NAT_FRAMEOFDATA,
ResponseKind::Status | ResponseKind::Value => NAT_RESPONSE,
}
}
fn remaining(deadline: Instant) -> io::Result<Duration> {
let duration = deadline.saturating_duration_since(Instant::now());
if duration.is_zero() {
Err(io::Error::new(
io::ErrorKind::TimedOut,
"command deadline elapsed",
))
} else {
Ok(duration)
}
}
fn invalid_input(message: impl fmt::Display) -> io::Error {
io::Error::new(io::ErrorKind::InvalidInput, message.to_string())
}
fn invalid_data(message: impl fmt::Display) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, message.to_string())
}
#[derive(Debug)]
pub enum HandshakeError {
Io(io::Error),
Command(command::CommandError),
Decode(NatNetError),
UnsupportedVersion {
info: ServerInfo,
source: NatNetError,
},
}
impl HandshakeError {
#[must_use]
pub fn server_info(&self) -> Option<&ServerInfo> {
match self {
Self::UnsupportedVersion { info, .. } => Some(info),
_ => None,
}
}
}
impl fmt::Display for HandshakeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(error) => write!(formatter, "command handshake I/O error: {error}"),
Self::Command(error) => write!(formatter, "command handshake request error: {error}"),
Self::Decode(error) => write!(formatter, "invalid SERVERINFO reply: {error}"),
Self::UnsupportedVersion { info, source } => {
write!(
formatter,
"unsupported stream protocol version (SERVERINFO reports {}): {source}",
info.protocol_version
)
}
}
}
}
impl std::error::Error for HandshakeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(error) => Some(error),
Self::Command(error) => Some(error),
Self::Decode(error) => Some(error),
Self::UnsupportedVersion { source, .. } => Some(source),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tier1Phase {
SetPropertyStatus,
GetPropertyReread,
}
#[derive(Debug)]
pub enum Tier1Failure {
Io(io::Error),
Command(command::CommandError),
}
impl fmt::Display for Tier1Failure {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(error) => error.fmt(formatter),
Self::Command(error) => error.fmt(formatter),
}
}
}
impl std::error::Error for Tier1Failure {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(error) => Some(error),
Self::Command(error) => Some(error),
}
}
}
#[derive(Debug)]
pub enum Tier1ExecutionError {
StatusFailure {
target: String,
status: i32,
progress: Vec<command::ProgressRecord>,
},
UnknownRemoteOutcome {
target: String,
phase: Tier1Phase,
source: Box<Tier1Failure>,
progress: Vec<command::ProgressRecord>,
},
}
impl Tier1ExecutionError {
#[must_use]
pub fn progress(&self) -> &[command::ProgressRecord] {
match self {
Self::StatusFailure { progress, .. } | Self::UnknownRemoteOutcome { progress, .. } => {
progress
}
}
}
}
impl fmt::Display for Tier1ExecutionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::StatusFailure { target, status, .. } => {
write!(
formatter,
"target {target:?} rejected write with status {status}"
)
}
Self::UnknownRemoteOutcome {
target,
phase,
source,
..
} => write!(
formatter,
"target {target:?} has unknown remote outcome during {phase:?}: {source}"
),
}
}
}
impl std::error::Error for Tier1ExecutionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::UnknownRemoteOutcome { source, .. } => Some(source.as_ref()),
Self::StatusFailure { .. } => None,
}
}
}
impl CommandClient {
pub fn execute_tier1(
&mut self,
write: command::Tier1Write,
timeout: Duration,
) -> Result<Vec<command::ProgressRecord>, Tier1ExecutionError> {
let mut step = write.begin();
let mut completed = Vec::new();
loop {
let target = step.target().to_owned();
let status = self
.exchange_inner(step.set_request(), timeout)
.map_err(|source| Tier1ExecutionError::UnknownRemoteOutcome {
target: target.clone(),
phase: Tier1Phase::SetPropertyStatus,
source: Box::new(Tier1Failure::Io(source)),
progress: completed.clone(),
})?;
match CommandResponse::parse(&status) {
Ok(CommandResponse::Status(0)) => {}
Ok(CommandResponse::Status(status)) => {
return Err(Tier1ExecutionError::StatusFailure {
target,
status,
progress: completed,
});
}
Ok(CommandResponse::Value(_)) => {
return Err(Tier1ExecutionError::UnknownRemoteOutcome {
target,
phase: Tier1Phase::SetPropertyStatus,
source: Box::new(Tier1Failure::Command(
command::CommandError::StatusExpected,
)),
progress: completed,
});
}
Err(source) => {
return Err(Tier1ExecutionError::UnknownRemoteOutcome {
target,
phase: Tier1Phase::SetPropertyStatus,
source: Box::new(Tier1Failure::Command(command::CommandError::Wire(
source,
))),
progress: completed,
});
}
}
let reread = self
.exchange_inner(step.reread_request(), timeout)
.map_err(|source| Tier1ExecutionError::UnknownRemoteOutcome {
target: target.clone(),
phase: Tier1Phase::GetPropertyReread,
source: Box::new(Tier1Failure::Io(source)),
progress: completed.clone(),
})?;
let verified = step.verify_status(&status, &reread).map_err(|source| {
Tier1ExecutionError::UnknownRemoteOutcome {
target: target.clone(),
phase: Tier1Phase::GetPropertyReread,
source: Box::new(Tier1Failure::Command(source)),
progress: completed.clone(),
}
})?;
let progress = verified.progress().to_vec();
match verified.next() {
Some(next) => {
completed = progress;
step = next;
}
None => return Ok(progress),
}
}
}
}