|
| 1 | +// TSI Stream Proxy for Windows |
| 2 | +// Handles TCP socket operations (connect, listen, accept) for guest |
| 3 | + |
| 4 | +use std::collections::HashMap; |
| 5 | +use std::io::{self, Read, Write}; |
| 6 | +use std::net::SocketAddr; |
| 7 | +use std::sync::{Arc, Mutex}; |
| 8 | + |
| 9 | +use super::socket_wrapper::{AddressFamily, ShutdownMode, SockType, WindowsSocket}; |
| 10 | +use crate::virtio::vsock::defs; |
| 11 | +use crate::virtio::vsock::packet::{TsiAcceptReq, TsiConnectReq, TsiListenReq, VsockPacket}; |
| 12 | +use crate::virtio::Queue as VirtQueue; |
| 13 | +use vm_memory::GuestMemoryMmap; |
| 14 | + |
| 15 | +/// Proxy status |
| 16 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 17 | +pub enum ProxyStatus { |
| 18 | + Init, |
| 19 | + Connecting, |
| 20 | + Connected, |
| 21 | + Listening, |
| 22 | + Closed, |
| 23 | +} |
| 24 | + |
| 25 | +/// Proxy error types |
| 26 | +#[derive(Debug)] |
| 27 | +pub enum ProxyError { |
| 28 | + InvalidFamily, |
| 29 | + CreatingSocket(io::Error), |
| 30 | + SettingNonBlocking(io::Error), |
| 31 | + SettingReuseAddr(io::Error), |
| 32 | + Binding(io::Error), |
| 33 | + Connecting(io::Error), |
| 34 | + Listening(io::Error), |
| 35 | + Accepting(io::Error), |
| 36 | + Sending(io::Error), |
| 37 | + Receiving(io::Error), |
| 38 | + InvalidState, |
| 39 | + InvalidAddress, |
| 40 | +} |
| 41 | + |
| 42 | +impl From<ProxyError> for io::Error { |
| 43 | + fn from(err: ProxyError) -> io::Error { |
| 44 | + match err { |
| 45 | + ProxyError::CreatingSocket(e) => e, |
| 46 | + ProxyError::SettingNonBlocking(e) => e, |
| 47 | + ProxyError::SettingReuseAddr(e) => e, |
| 48 | + ProxyError::Binding(e) => e, |
| 49 | + ProxyError::Connecting(e) => e, |
| 50 | + ProxyError::Listening(e) => e, |
| 51 | + ProxyError::Accepting(e) => e, |
| 52 | + ProxyError::Sending(e) => e, |
| 53 | + ProxyError::Receiving(e) => e, |
| 54 | + _ => io::Error::new(io::ErrorKind::Other, format!("{:?}", err)), |
| 55 | + } |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +/// TSI Stream Proxy for Windows |
| 60 | +pub struct TsiStreamProxyWindows { |
| 61 | + id: u64, |
| 62 | + cid: u64, |
| 63 | + family: AddressFamily, |
| 64 | + local_port: u32, |
| 65 | + peer_port: u32, |
| 66 | + control_port: u32, |
| 67 | + socket: WindowsSocket, |
| 68 | + pub status: ProxyStatus, |
| 69 | + mem: GuestMemoryMmap, |
| 70 | + queue: Arc<Mutex<VirtQueue>>, |
| 71 | + // Pending accept connections for listening sockets |
| 72 | + pending_accepts: Vec<(WindowsSocket, SocketAddr)>, |
| 73 | +} |
| 74 | + |
| 75 | +impl TsiStreamProxyWindows { |
| 76 | + /// Create a new TSI Stream Proxy |
| 77 | + pub fn new( |
| 78 | + id: u64, |
| 79 | + cid: u64, |
| 80 | + family: u16, |
| 81 | + local_port: u32, |
| 82 | + peer_port: u32, |
| 83 | + control_port: u32, |
| 84 | + mem: GuestMemoryMmap, |
| 85 | + queue: Arc<Mutex<VirtQueue>>, |
| 86 | + ) -> Result<Self, ProxyError> { |
| 87 | + // Convert Linux address family to Windows |
| 88 | + let family = match family { |
| 89 | + defs::LINUX_AF_INET => AddressFamily::Inet, |
| 90 | + defs::LINUX_AF_INET6 => AddressFamily::Inet6, |
| 91 | + _ => return Err(ProxyError::InvalidFamily), |
| 92 | + }; |
| 93 | + |
| 94 | + // Create socket |
| 95 | + let socket = WindowsSocket::new(family, SockType::Stream) |
| 96 | + .map_err(ProxyError::CreatingSocket)?; |
| 97 | + |
| 98 | + // Set non-blocking mode |
| 99 | + socket |
| 100 | + .set_nonblocking(true) |
| 101 | + .map_err(ProxyError::SettingNonBlocking)?; |
| 102 | + |
| 103 | + // Set SO_REUSEADDR |
| 104 | + socket |
| 105 | + .set_reuseaddr(true) |
| 106 | + .map_err(ProxyError::SettingReuseAddr)?; |
| 107 | + |
| 108 | + Ok(Self { |
| 109 | + id, |
| 110 | + cid, |
| 111 | + family, |
| 112 | + local_port, |
| 113 | + peer_port, |
| 114 | + control_port, |
| 115 | + socket, |
| 116 | + status: ProxyStatus::Init, |
| 117 | + mem, |
| 118 | + queue, |
| 119 | + pending_accepts: Vec::new(), |
| 120 | + }) |
| 121 | + } |
| 122 | + |
| 123 | + /// Get proxy ID |
| 124 | + pub fn id(&self) -> u64 { |
| 125 | + self.id |
| 126 | + } |
| 127 | + |
| 128 | + /// Get local port |
| 129 | + pub fn local_port(&self) -> u32 { |
| 130 | + self.local_port |
| 131 | + } |
| 132 | + |
| 133 | + /// Process TSI_CONNECT request |
| 134 | + pub fn process_connect(&mut self, req: &TsiConnectReq) -> Result<(), ProxyError> { |
| 135 | + if self.status != ProxyStatus::Init { |
| 136 | + return Err(ProxyError::InvalidState); |
| 137 | + } |
| 138 | + |
| 139 | + // Parse address from request |
| 140 | + let addr = parse_address(req.family, &req.addr, req.port) |
| 141 | + .ok_or(ProxyError::InvalidAddress)?; |
| 142 | + |
| 143 | + // Connect to remote address |
| 144 | + self.socket |
| 145 | + .connect(&addr) |
| 146 | + .map_err(ProxyError::Connecting)?; |
| 147 | + |
| 148 | + self.status = ProxyStatus::Connecting; |
| 149 | + |
| 150 | + // Note: Connection may complete asynchronously |
| 151 | + // Caller should check socket status later |
| 152 | + |
| 153 | + Ok(()) |
| 154 | + } |
| 155 | + |
| 156 | + /// Process TSI_LISTEN request |
| 157 | + pub fn process_listen(&mut self, req: &TsiListenReq) -> Result<(), ProxyError> { |
| 158 | + if self.status != ProxyStatus::Init { |
| 159 | + return Err(ProxyError::InvalidState); |
| 160 | + } |
| 161 | + |
| 162 | + // Parse bind address from request |
| 163 | + let addr = parse_address(req.family, &req.addr, req.port) |
| 164 | + .ok_or(ProxyError::InvalidAddress)?; |
| 165 | + |
| 166 | + // Bind to address |
| 167 | + self.socket.bind(&addr).map_err(ProxyError::Binding)?; |
| 168 | + |
| 169 | + // Listen with specified backlog |
| 170 | + self.socket |
| 171 | + .listen(req.backlog as i32) |
| 172 | + .map_err(ProxyError::Listening)?; |
| 173 | + |
| 174 | + self.status = ProxyStatus::Listening; |
| 175 | + |
| 176 | + Ok(()) |
| 177 | + } |
| 178 | + |
| 179 | + /// Process TSI_ACCEPT request |
| 180 | + pub fn process_accept(&mut self) -> Result<Option<(u64, SocketAddr)>, ProxyError> { |
| 181 | + if self.status != ProxyStatus::Listening { |
| 182 | + return Err(ProxyError::InvalidState); |
| 183 | + } |
| 184 | + |
| 185 | + // Try to accept a connection |
| 186 | + match self.socket.accept() { |
| 187 | + Ok((client_socket, client_addr)) => { |
| 188 | + // Set non-blocking mode for client socket |
| 189 | + client_socket |
| 190 | + .set_nonblocking(true) |
| 191 | + .map_err(ProxyError::SettingNonBlocking)?; |
| 192 | + |
| 193 | + // Store pending accept |
| 194 | + self.pending_accepts.push((client_socket, client_addr)); |
| 195 | + |
| 196 | + // Generate new connection ID |
| 197 | + let conn_id = self.id + self.pending_accepts.len() as u64; |
| 198 | + |
| 199 | + Ok(Some((conn_id, client_addr))) |
| 200 | + } |
| 201 | + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { |
| 202 | + // No pending connections |
| 203 | + Ok(None) |
| 204 | + } |
| 205 | + Err(e) => Err(ProxyError::Accepting(e)), |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + /// Send data to remote peer |
| 210 | + pub fn send_data(&mut self, data: &[u8]) -> Result<usize, ProxyError> { |
| 211 | + if self.status != ProxyStatus::Connected { |
| 212 | + return Err(ProxyError::InvalidState); |
| 213 | + } |
| 214 | + |
| 215 | + self.socket.send(data).map_err(ProxyError::Sending) |
| 216 | + } |
| 217 | + |
| 218 | + /// Receive data from remote peer |
| 219 | + pub fn recv_data(&mut self, buf: &mut [u8]) -> Result<usize, ProxyError> { |
| 220 | + if self.status != ProxyStatus::Connected { |
| 221 | + return Err(ProxyError::InvalidState); |
| 222 | + } |
| 223 | + |
| 224 | + match self.socket.recv(buf) { |
| 225 | + Ok(n) => Ok(n), |
| 226 | + Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0), |
| 227 | + Err(e) => Err(ProxyError::Receiving(e)), |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + /// Check if connection is established (for async connect) |
| 232 | + pub fn check_connected(&mut self) -> Result<bool, ProxyError> { |
| 233 | + if self.status != ProxyStatus::Connecting { |
| 234 | + return Ok(false); |
| 235 | + } |
| 236 | + |
| 237 | + // Try to get peer address to check if connected |
| 238 | + match self.socket.peer_addr() { |
| 239 | + Ok(_) => { |
| 240 | + self.status = ProxyStatus::Connected; |
| 241 | + Ok(true) |
| 242 | + } |
| 243 | + Err(e) if e.kind() == io::ErrorKind::NotConnected => Ok(false), |
| 244 | + Err(e) => Err(ProxyError::Connecting(e)), |
| 245 | + } |
| 246 | + } |
| 247 | + |
| 248 | + /// Shutdown the connection |
| 249 | + pub fn shutdown(&mut self, mode: ShutdownMode) -> Result<(), ProxyError> { |
| 250 | + self.socket |
| 251 | + .shutdown(mode) |
| 252 | + .map_err(|e| ProxyError::Sending(e)) |
| 253 | + } |
| 254 | + |
| 255 | + /// Close the proxy |
| 256 | + pub fn close(&mut self) { |
| 257 | + self.status = ProxyStatus::Closed; |
| 258 | + // Socket will be closed automatically by Drop |
| 259 | + } |
| 260 | +} |
| 261 | + |
| 262 | +/// Parse address from TSI request |
| 263 | +fn parse_address(family: u16, addr_bytes: &[u8], port: u16) -> Option<SocketAddr> { |
| 264 | + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
| 265 | + |
| 266 | + match family { |
| 267 | + defs::LINUX_AF_INET => { |
| 268 | + if addr_bytes.len() < 4 { |
| 269 | + return None; |
| 270 | + } |
| 271 | + let ip = Ipv4Addr::new(addr_bytes[0], addr_bytes[1], addr_bytes[2], addr_bytes[3]); |
| 272 | + Some(SocketAddr::new(IpAddr::V4(ip), port)) |
| 273 | + } |
| 274 | + defs::LINUX_AF_INET6 => { |
| 275 | + if addr_bytes.len() < 16 { |
| 276 | + return None; |
| 277 | + } |
| 278 | + let mut octets = [0u8; 16]; |
| 279 | + octets.copy_from_slice(&addr_bytes[0..16]); |
| 280 | + let ip = Ipv6Addr::from(octets); |
| 281 | + Some(SocketAddr::new(IpAddr::V6(ip), port)) |
| 282 | + } |
| 283 | + _ => None, |
| 284 | + } |
| 285 | +} |
| 286 | + |
| 287 | +#[cfg(test)] |
| 288 | +mod tests { |
| 289 | + use super::*; |
| 290 | + |
| 291 | + #[test] |
| 292 | + fn test_parse_ipv4_address() { |
| 293 | + let addr_bytes = [127, 0, 0, 1]; |
| 294 | + let addr = parse_address(defs::LINUX_AF_INET, &addr_bytes, 8080); |
| 295 | + assert!(addr.is_some()); |
| 296 | + let addr = addr.unwrap(); |
| 297 | + assert_eq!(addr.port(), 8080); |
| 298 | + assert_eq!(addr.ip().to_string(), "127.0.0.1"); |
| 299 | + } |
| 300 | + |
| 301 | + #[test] |
| 302 | + fn test_parse_ipv6_address() { |
| 303 | + let addr_bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; |
| 304 | + let addr = parse_address(defs::LINUX_AF_INET6, &addr_bytes, 8080); |
| 305 | + assert!(addr.is_some()); |
| 306 | + let addr = addr.unwrap(); |
| 307 | + assert_eq!(addr.port(), 8080); |
| 308 | + assert_eq!(addr.ip().to_string(), "::1"); |
| 309 | + } |
| 310 | +} |
0 commit comments