kernel/network/
socket.rs

1//! Socket abstraction and common types
2//!
3//! This module provides the core socket abstraction for Scarlet's network functionality.
4//! Following Scarlet's philosophy, it defines OS-agnostic socket operations that can be
5//! used for both internal IPC and external network communication.
6//!
7//! # Design Philosophy
8//!
9//! Like TTY devices, Scarlet Sockets use neutral, OS-agnostic abstractions:
10//! - Scarlet-private control opcodes (SCTL_SOCKET_*) instead of OS-specific ioctls
11//! - ABI modules translate their specific syscalls to these neutral operations
12//! - Works for both internal (process-to-process) and external (network) communication
13//!
14//! # Control Opcodes
15//!
16//! Socket control operations use the SCTL_SOCKET_* namespace (magic 'SS' = 0x53, 0x53)
17
18use alloc::{string::String, sync::Arc};
19
20use crate::ipc::StreamIpcOps;
21use crate::object::capability::Selectable;
22
23/// Scarlet-private, OS-agnostic control opcodes for Socket operations.
24/// These are stable only within Scarlet and must be mapped by ABI adapters.
25pub mod socket_ctl {
26    /// Magic 'SS' (0x53, 0x53) followed by sequential IDs to avoid collisions.
27    ///
28    /// Bind socket to an address (arg = address structure pointer)
29    pub const SCTL_SOCKET_BIND: u32 = 0x5353_0001;
30    /// Connect to remote address (arg = address structure pointer)
31    pub const SCTL_SOCKET_CONNECT: u32 = 0x5353_0002;
32    /// Start listening for connections (arg = backlog size)
33    pub const SCTL_SOCKET_LISTEN: u32 = 0x5353_0003;
34    /// Get local address (arg = buffer pointer for address)
35    pub const SCTL_SOCKET_GETSOCKNAME: u32 = 0x5353_0004;
36    /// Get peer address (arg = buffer pointer for address)
37    pub const SCTL_SOCKET_GETPEERNAME: u32 = 0x5353_0005;
38    /// Shutdown socket (arg: 0=read, 1=write, 2=both)
39    pub const SCTL_SOCKET_SHUTDOWN: u32 = 0x5353_0006;
40    /// Set socket to non-blocking mode (arg: 0=blocking, 1=non-blocking)
41    pub const SCTL_SOCKET_SET_NONBLOCK: u32 = 0x5353_0007;
42    /// Get socket state (returns SocketState value)
43    pub const SCTL_SOCKET_GET_STATE: u32 = 0x5353_0008;
44    /// Get socket non-blocking mode (returns 0 or 1)
45    pub const SCTL_SOCKET_GET_NONBLOCK: u32 = 0x5353_000B;
46    /// Get socket type (returns SocketType value)
47    pub const SCTL_SOCKET_GET_TYPE: u32 = 0x5353_0009;
48    /// Check if connected (returns 0 or 1)
49    pub const SCTL_SOCKET_IS_CONNECTED: u32 = 0x5353_000A;
50}
51
52/// Socket type enumeration
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54#[repr(u32)]
55pub enum SocketType {
56    /// Stream socket (connection-oriented, reliable, byte stream)
57    Stream = 1,
58    /// Datagram socket (connectionless, unreliable, message-oriented)
59    Datagram = 2,
60    /// Raw socket (direct protocol access)
61    Raw = 3,
62    /// Sequenced packet socket (connection-oriented, reliable, message-oriented)
63    SeqPacket = 4,
64}
65
66/// Socket domain (address family) - neutral to any specific OS
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
68#[repr(u32)]
69pub enum SocketDomain {
70    /// Local inter-process communication
71    /// (Unix domain socket equivalent, but OS-agnostic)
72    Local = 1,
73    /// IPv4 Internet protocols
74    Inet4 = 2,
75    /// IPv6 Internet protocols
76    Inet6 = 3,
77    /// Packet-level communication
78    Packet = 4,
79}
80
81/// Socket protocol
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83#[repr(u32)]
84pub enum SocketProtocol {
85    /// Default protocol for socket type/domain combination
86    Default = 0,
87    /// TCP protocol
88    Tcp = 6,
89    /// UDP protocol  
90    Udp = 17,
91    /// ICMP protocol
92    Icmp = 1,
93    /// Raw protocol with specific number
94    Raw(u16) = 255,
95}
96
97/// Socket address abstraction - OS-agnostic
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum SocketAddress {
100    /// Local IPC address (path or abstract name)
101    Local(LocalSocketAddress),
102    /// IPv4 address with port
103    Inet(Inet4SocketAddress),
104    /// IPv6 address with port
105    Inet6(Inet6SocketAddress),
106    /// Unspecified/any address
107    Unspecified,
108}
109
110impl SocketAddress {
111    /// Check if this is an unspecified address
112    pub fn is_unspecified(&self) -> bool {
113        matches!(self, SocketAddress::Unspecified)
114    }
115
116    /// Get the domain of this address
117    pub fn domain(&self) -> SocketDomain {
118        match self {
119            SocketAddress::Local(_) => SocketDomain::Local,
120            SocketAddress::Inet(_) => SocketDomain::Inet4,
121            SocketAddress::Inet6(_) => SocketDomain::Inet6,
122            SocketAddress::Unspecified => SocketDomain::Local, // Default
123        }
124    }
125}
126
127/// Local socket address for inter-process communication
128/// (OS-agnostic, not tied to Unix specifically)
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct LocalSocketAddress {
131    /// Socket path or name (may be empty for unnamed sockets)
132    path: String,
133    /// Whether this is an abstract name (not filesystem-based)
134    abstract_name: bool,
135}
136
137impl LocalSocketAddress {
138    /// Create a local socket address from a path
139    pub fn from_path(path: impl Into<String>) -> Result<Self, SocketError> {
140        let path = path.into();
141        if path.len() > 108 {
142            // Common socket path length limit
143            return Err(SocketError::InvalidAddress);
144        }
145        Ok(Self {
146            path,
147            abstract_name: false,
148        })
149    }
150
151    /// Create an abstract local socket address (not filesystem-based)
152    pub fn from_abstract(name: impl Into<String>) -> Result<Self, SocketError> {
153        let name = name.into();
154        if name.len() > 107 {
155            return Err(SocketError::InvalidAddress);
156        }
157        Ok(Self {
158            path: name,
159            abstract_name: true,
160        })
161    }
162
163    /// Create an unnamed/anonymous local socket address
164    pub fn unnamed() -> Self {
165        Self {
166            path: String::new(),
167            abstract_name: false,
168        }
169    }
170
171    /// Get the socket path or name
172    pub fn path(&self) -> &str {
173        &self.path
174    }
175
176    /// Check if this is an unnamed socket
177    pub fn is_unnamed(&self) -> bool {
178        self.path.is_empty()
179    }
180
181    /// Check if this is an abstract name
182    pub fn is_abstract(&self) -> bool {
183        self.abstract_name
184    }
185}
186
187// Keep Unix-named type for backwards compatibility with documentation
188pub type UnixSocketAddress = LocalSocketAddress;
189
190/// IPv4 socket address
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub struct Inet4SocketAddress {
193    /// IPv4 address
194    pub addr: [u8; 4],
195    /// Port number
196    pub port: u16,
197}
198
199impl Inet4SocketAddress {
200    /// Create a new IPv4 socket address
201    pub fn new(addr: [u8; 4], port: u16) -> Self {
202        Self { addr, port }
203    }
204}
205
206/// IPv6 socket address
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub struct Inet6SocketAddress {
209    /// IPv6 address
210    pub addr: [u8; 16],
211    /// Port number
212    pub port: u16,
213    /// Flow information
214    pub flowinfo: u32,
215    /// Scope ID
216    pub scope_id: u32,
217}
218
219impl Inet6SocketAddress {
220    /// Create a new IPv6 socket address
221    pub fn new(addr: [u8; 16], port: u16) -> Self {
222        Self {
223            addr,
224            port,
225            flowinfo: 0,
226            scope_id: 0,
227        }
228    }
229}
230
231/// Socket shutdown directions
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233#[repr(u32)]
234pub enum ShutdownHow {
235    /// Shutdown reading
236    Read = 0,
237    /// Shutdown writing
238    Write = 1,
239    /// Shutdown both reading and writing
240    Both = 2,
241}
242
243/// Socket state
244#[derive(Debug, Clone, Copy, PartialEq, Eq)]
245#[repr(u32)]
246pub enum SocketState {
247    /// Socket is unbound and unconnected
248    Unconnected = 0,
249    /// Socket is bound to an address
250    Bound = 1,
251    /// Socket is listening for connections
252    Listening = 2,
253    /// Socket is connecting (for non-blocking sockets)
254    Connecting = 3,
255    /// Socket is connected
256    Connected = 4,
257    /// Socket is disconnecting
258    Disconnecting = 5,
259    /// Socket is closed
260    Closed = 6,
261}
262
263/// Socket errors
264#[derive(Debug, Clone, PartialEq, Eq)]
265pub enum SocketError {
266    /// Invalid socket address
267    InvalidAddress,
268    /// Address already in use
269    AddressInUse,
270    /// Address not available
271    AddressNotAvailable,
272    /// Connection refused
273    ConnectionRefused,
274    /// Connection reset by peer
275    ConnectionReset,
276    /// Connection aborted
277    ConnectionAborted,
278    /// Not connected
279    NotConnected,
280    /// Already connected
281    AlreadyConnected,
282    /// Invalid operation for socket state
283    InvalidOperation,
284    /// Socket is not listening
285    NotListening,
286    /// No pending connections
287    NoConnections,
288    /// Operation would block
289    WouldBlock,
290    /// Invalid argument
291    InvalidArgument,
292    /// Not supported
293    NotSupported,
294    /// No route to destination
295    NoRoute,
296    /// Protocol not supported
297    ProtocolNotSupported,
298    /// Invalid packet format
299    InvalidPacket,
300    /// Custom error message
301    Other(String),
302}
303
304/// Socket control operations trait
305///
306/// Provides OS-agnostic socket control, similar to TtyControl for TTY devices.
307/// ABI modules translate their specific socket operations to these neutral controls.
308pub trait SocketControl {
309    /// Bind socket to an address
310    fn bind(&self, address: &SocketAddress) -> Result<(), SocketError>;
311
312    /// Connect to a remote address
313    fn connect(&self, address: &SocketAddress) -> Result<(), SocketError>;
314
315    /// Listen for incoming connections (for stream sockets)
316    fn listen(&self, backlog: usize) -> Result<(), SocketError>;
317
318    /// Accept an incoming connection (for listening sockets)
319    /// Returns a new socket for the accepted connection
320    fn accept(&self) -> Result<Arc<dyn SocketObject>, SocketError>;
321
322    /// Get socket peer address
323    fn getpeername(&self) -> Result<SocketAddress, SocketError>;
324
325    /// Get socket local address  
326    fn getsockname(&self) -> Result<SocketAddress, SocketError>;
327
328    /// Shutdown socket for reading, writing, or both
329    fn shutdown(&self, how: ShutdownHow) -> Result<(), SocketError>;
330
331    /// Check if socket is connected
332    fn is_connected(&self) -> bool;
333
334    /// Get socket state
335    fn state(&self) -> SocketState;
336}
337
338/// Socket operations trait
339///
340/// Combines StreamIpcOps (for data transfer), SocketControl (for connection management),
341/// and CloneOps (for handle duplication). This is the main trait that socket implementations
342/// must satisfy.
343///
344/// Similar to how TtyDeviceEndpoint combines CharDevice + TtyControl.
345pub trait SocketObject: StreamIpcOps + SocketControl + Send + Sync {
346    /// Get socket type (Stream, Datagram, etc.)
347    fn socket_type(&self) -> SocketType;
348
349    /// Get socket domain (Local, Inet, Inet6, etc.)
350    fn socket_domain(&self) -> SocketDomain;
351
352    /// Get socket protocol
353    fn socket_protocol(&self) -> SocketProtocol;
354
355    /// Cast to Any for safe downcasting
356    fn as_any(&self) -> &dyn core::any::Any
357    where
358        Self: 'static;
359
360    /// Send data to a specific address (for datagram sockets)
361    /// For stream sockets, address is ignored and data is sent to connected peer
362    fn sendto(
363        &self,
364        data: &[u8],
365        address: &SocketAddress,
366        flags: u32,
367    ) -> Result<usize, SocketError> {
368        let _ = (address, flags);
369        // Default implementation for stream sockets - ignore address
370        self.write(data).map_err(|_| SocketError::NotSupported)
371    }
372
373    /// Receive data with source address (for datagram sockets)
374    /// For stream sockets, returns Unspecified address
375    fn recvfrom(
376        &self,
377        buffer: &mut [u8],
378        flags: u32,
379    ) -> Result<(usize, SocketAddress), SocketError> {
380        let _ = flags;
381        // Default implementation for stream sockets
382        let n = self.read(buffer).map_err(|_| SocketError::NotSupported)?;
383        Ok((n, SocketAddress::Unspecified))
384    }
385
386    /// Optional capability: expose select/pselect readiness/wait interface
387    fn as_selectable(&self) -> Option<&dyn Selectable> {
388        None
389    }
390
391    /// Optional capability: expose control operations interface
392    fn as_control_ops(&self) -> Option<&dyn crate::object::capability::ControlOps> {
393        None
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[test_case]
402    fn test_socket_type() {
403        assert_eq!(SocketType::Stream, SocketType::Stream);
404        assert_ne!(SocketType::Stream, SocketType::Datagram);
405    }
406
407    #[test_case]
408    fn test_socket_domain() {
409        assert_eq!(SocketDomain::Local, SocketDomain::Local);
410        assert_ne!(SocketDomain::Local, SocketDomain::Inet4);
411    }
412
413    #[test_case]
414    fn test_local_socket_address() {
415        let addr = LocalSocketAddress::from_path("/tmp/test.sock").unwrap();
416        assert_eq!(addr.path(), "/tmp/test.sock");
417        assert!(!addr.is_unnamed());
418        assert!(!addr.is_abstract());
419
420        let unnamed = LocalSocketAddress::unnamed();
421        assert!(unnamed.is_unnamed());
422        assert_eq!(unnamed.path(), "");
423
424        let abstract_addr = LocalSocketAddress::from_abstract("test").unwrap();
425        assert!(abstract_addr.is_abstract());
426        assert_eq!(abstract_addr.path(), "test");
427    }
428
429    #[test_case]
430    fn test_local_socket_address_too_long() {
431        let long_path = "a".repeat(109);
432        assert!(LocalSocketAddress::from_path(long_path).is_err());
433    }
434
435    #[test_case]
436    fn test_inet4_socket_address() {
437        let addr = Inet4SocketAddress::new([127, 0, 0, 1], 8080);
438        assert_eq!(addr.addr, [127, 0, 0, 1]);
439        assert_eq!(addr.port, 8080);
440    }
441
442    #[test_case]
443    fn test_socket_address_domain() {
444        let local_addr = SocketAddress::Local(LocalSocketAddress::unnamed());
445        assert_eq!(local_addr.domain(), SocketDomain::Local);
446
447        let inet_addr = SocketAddress::Inet(Inet4SocketAddress::new([127, 0, 0, 1], 8080));
448        assert_eq!(inet_addr.domain(), SocketDomain::Inet4);
449    }
450
451    #[test_case]
452    fn test_socket_state() {
453        assert_eq!(SocketState::Unconnected, SocketState::Unconnected);
454        assert_ne!(SocketState::Unconnected, SocketState::Connected);
455    }
456
457    #[test_case]
458    fn test_shutdown_how() {
459        assert_eq!(ShutdownHow::Read, ShutdownHow::Read);
460        assert_ne!(ShutdownHow::Read, ShutdownHow::Write);
461    }
462}