pub mod frame;
pub mod proto;
pub mod session;
pub use session::VncSession;
use std::io::{ErrorKind, Read, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use crate::host::display::Surface;
use crate::host::input::{InputEvent, Keysym};
use crate::host::listen;
use frame::FrameEncoder;
use proto::{ClientMessage, Parsed, PixelFormat, Version};
pub const MAX_CLIENTS: usize = 8;
const MAX_PENDING: usize = 8 * 1024 * 1024;
const MAX_INBOX: usize = 64 * 1024;
const DEFAULT_NAME: &str = "rsemu";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Phase {
Version,
Security,
Init,
Ready,
}
#[derive(Debug)]
struct Conn {
stream: TcpStream,
peer: Option<SocketAddr>,
phase: Phase,
version: Version,
inbox: Vec<u8>,
pending: Vec<u8>,
encoder: FrameEncoder,
wants_full: bool,
wants_incremental: bool,
shared: bool,
}
impl Conn {
fn format(&self) -> PixelFormat {
self.encoder.format()
}
}
#[derive(Debug)]
pub struct VncServer {
listener: TcpListener,
conns: Vec<Conn>,
name: String,
geometry: (u16, u16),
}
impl VncServer {
pub fn bind(addr: &str) -> std::io::Result<VncServer> {
Ok(VncServer {
listener: listen::bind(addr)?,
conns: Vec::new(),
name: String::from(DEFAULT_NAME),
geometry: (1, 1),
})
}
#[must_use]
pub fn named(mut self, name: &str) -> VncServer {
self.name = name.to_string();
self
}
pub fn set_geometry(&mut self, width: u32, height: u32) {
self.geometry = (clamp16(width).max(1), clamp16(height).max(1));
}
pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
self.listener.local_addr()
}
#[must_use]
pub fn clients(&self) -> usize {
self.conns.len()
}
#[must_use]
pub fn is_watched(&self) -> bool {
!self.conns.is_empty()
}
#[must_use]
pub fn peers(&self) -> Vec<SocketAddr> {
self.conns.iter().filter_map(|c| c.peer).collect()
}
pub fn poll(&mut self, surface: &Surface) -> std::io::Result<Vec<InputEvent>> {
self.accept()?;
let mut events = Vec::new();
let mut i = 0;
while i < self.conns.len() {
if Self::service(&mut self.conns[i], surface, &self.name, &mut events) {
i += 1;
} else {
self.conns.remove(i);
}
}
Ok(events)
}
pub fn bell(&mut self) {
for conn in &mut self.conns {
conn.pending.extend_from_slice(&proto::bell());
}
}
fn accept(&mut self) -> std::io::Result<()> {
loop {
match self.listener.accept() {
Ok((stream, peer)) => {
if self.conns.len() >= MAX_CLIENTS {
drop(stream);
continue;
}
stream.set_nonblocking(true)?;
let _ = stream.set_nodelay(true);
let (width, height) = self.geometry;
let mut conn = Conn {
stream,
peer: Some(peer),
phase: Phase::Version,
version: Version::V3_8,
inbox: Vec::new(),
pending: Vec::new(),
encoder: FrameEncoder::new(PixelFormat::DEFAULT, width, height),
wants_full: false,
wants_incremental: false,
shared: true,
};
conn.pending.extend_from_slice(proto::VERSION_3_8);
self.conns.push(conn);
}
Err(e) if e.kind() == ErrorKind::WouldBlock => return Ok(()),
Err(e) if e.kind() == ErrorKind::Interrupted => return Ok(()),
Err(e) => return Err(e),
}
}
}
fn service(
conn: &mut Conn,
surface: &Surface,
name: &str,
events: &mut Vec<InputEvent>,
) -> bool {
let mut buf = [0u8; 4096];
loop {
match conn.stream.read(&mut buf) {
Ok(0) => return false,
Ok(n) => {
conn.inbox.extend_from_slice(&buf[..n]);
if n < buf.len() {
break;
}
}
Err(e) if e.kind() == ErrorKind::WouldBlock => break,
Err(e) if e.kind() == ErrorKind::Interrupted => {}
Err(_) => return false,
}
}
if !Self::consume(conn, name, events) {
return false;
}
if conn.inbox.len() > MAX_INBOX {
return false;
}
Self::produce(conn, surface);
Self::flush(conn)
}
fn consume(conn: &mut Conn, name: &str, events: &mut Vec<InputEvent>) -> bool {
loop {
match conn.phase {
Phase::Version => {
if conn.inbox.len() < proto::VERSION_LEN {
return true;
}
let Some(version) = Version::parse(&conn.inbox[..proto::VERSION_LEN]) else {
return false;
};
conn.inbox.drain(..proto::VERSION_LEN);
conn.version = version;
if version >= Version::V3_7 {
conn.pending.extend_from_slice(&proto::security_types());
conn.phase = Phase::Security;
} else {
conn.pending.extend_from_slice(&proto::security_type_3_3());
conn.phase = Phase::Init;
}
}
Phase::Security => {
let Some(&choice) = conn.inbox.first() else {
return true;
};
conn.inbox.drain(..1);
if choice != proto::SECURITY_NONE {
if conn.version >= Version::V3_8 {
conn.pending
.extend_from_slice(&proto::security_result_failed(
"rsemu offers the None security type only",
));
}
let _ = conn.stream.write_all(&conn.pending);
return false;
}
if conn.version >= Version::V3_8 {
conn.pending.extend_from_slice(&proto::security_result_ok());
}
conn.phase = Phase::Init;
}
Phase::Init => {
let Some(&shared) = conn.inbox.first() else {
return true;
};
conn.inbox.drain(..1);
conn.shared = shared != 0;
let (width, height) = conn.encoder.announced();
conn.pending.extend_from_slice(&proto::server_init(
width,
height,
conn.format(),
name,
));
conn.phase = Phase::Ready;
}
Phase::Ready => match proto::parse_client(&conn.inbox) {
Parsed::Incomplete => return true,
Parsed::Unknown(_) => return false,
Parsed::Message(message, used) => {
conn.inbox.drain(..used);
Self::apply(conn, message, events);
}
},
}
}
}
fn apply(conn: &mut Conn, message: ClientMessage, events: &mut Vec<InputEvent>) {
match message {
ClientMessage::SetPixelFormat(format) => {
if format.is_supported() {
conn.encoder.set_format(format);
}
}
ClientMessage::SetEncodings(list) => conn.encoder.set_encodings(&list),
ClientMessage::UpdateRequest { incremental, .. } => {
if incremental {
conn.wants_incremental = true;
} else {
conn.wants_full = true;
}
}
ClientMessage::Key { key, down } => events.push(InputEvent::Key {
keysym: Keysym(key),
down,
}),
ClientMessage::Pointer { x, y, buttons } => events.push(InputEvent::Pointer {
x: u32::from(x),
y: u32::from(y),
buttons,
}),
ClientMessage::CutText(_) => {}
}
}
fn produce(conn: &mut Conn, surface: &Surface) {
if conn.phase != Phase::Ready || conn.pending.len() > MAX_PENDING {
return;
}
if conn.wants_full {
if let Some(update) = conn.encoder.update(surface, false) {
conn.pending.extend_from_slice(&update);
}
conn.wants_full = false;
conn.wants_incremental = false;
} else if conn.wants_incremental {
if let Some(update) = conn.encoder.update(surface, true) {
conn.pending.extend_from_slice(&update);
conn.wants_incremental = false;
}
}
}
fn flush(conn: &mut Conn) -> bool {
while !conn.pending.is_empty() {
match conn.stream.write(&conn.pending) {
Ok(0) => return false,
Ok(n) => {
conn.pending.drain(..n);
}
Err(e) if e.kind() == ErrorKind::WouldBlock => return true,
Err(e) if e.kind() == ErrorKind::Interrupted => {}
Err(_) => return false,
}
}
let _ = conn.stream.flush();
true
}
}
#[inline]
const fn clamp16(value: u32) -> u16 {
if value > u16::MAX as u32 {
u16::MAX
} else {
value as u16
}
}
#[cfg(test)]
mod tests;