223 lines
6.9 KiB
Rust
223 lines
6.9 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use core::fmt::Display;
|
|
|
|
use options::SocketOption;
|
|
use util::{MessageHeader, SendRecvFlags, SockShutdownCmd, SocketAddr};
|
|
|
|
use crate::{
|
|
fs::{
|
|
file_handle::FileLike,
|
|
file_table::FdFlags,
|
|
path::RESERVED_MOUNT_ID,
|
|
pseudofs::{sockfs_singleton, PseudoInode},
|
|
utils::{mkmod, CreationFlags, Inode, InodeType, StatusFlags},
|
|
},
|
|
prelude::*,
|
|
process::{Gid, Uid},
|
|
util::{MultiRead, MultiWrite},
|
|
};
|
|
|
|
pub mod ip;
|
|
pub mod netlink;
|
|
pub mod options;
|
|
pub mod unix;
|
|
pub mod util;
|
|
pub mod vsock;
|
|
|
|
mod private {
|
|
use crate::{events::IoEvents, prelude::*, process::signal::Pollable};
|
|
|
|
/// Common methods for sockets, but private to the network module.
|
|
///
|
|
/// These are implementation details of sockets, so shouldn't be accessed outside the network
|
|
/// module. Therefore, the whole trait is sealed.
|
|
pub trait SocketPrivate: Pollable {
|
|
/// Returns whether the socket is in non-blocking mode.
|
|
fn is_nonblocking(&self) -> bool;
|
|
|
|
/// Sets whether the socket is in non-blocking mode.
|
|
fn set_nonblocking(&self, nonblocking: bool);
|
|
|
|
/// Blocks until some events occur to complete I/O operations.
|
|
///
|
|
/// If the socket is in non-blocking mode and the I/O operations cannot be completed
|
|
/// immediately, this method will fail with [`EAGAIN`] instead of blocking.
|
|
///
|
|
/// [`EAGAIN`]: crate::error::Errno::EAGAIN
|
|
#[track_caller]
|
|
fn block_on<F, R>(&self, events: IoEvents, mut try_op: F) -> Result<R>
|
|
where
|
|
Self: Sized,
|
|
F: FnMut() -> Result<R>,
|
|
{
|
|
if self.is_nonblocking() {
|
|
try_op()
|
|
} else {
|
|
self.wait_events(events, None, try_op)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Operations defined on a socket.
|
|
pub trait Socket: private::SocketPrivate + Send + Sync {
|
|
/// Assigns the specified address to the socket.
|
|
fn bind(&self, _socket_addr: SocketAddr) -> Result<()> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "bind() is not supported");
|
|
}
|
|
|
|
/// Builds a connection for the given address
|
|
fn connect(&self, _socket_addr: SocketAddr) -> Result<()> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "connect() is not supported");
|
|
}
|
|
|
|
/// Listens for connections on the socket.
|
|
fn listen(&self, _backlog: usize) -> Result<()> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "listen() is not supported");
|
|
}
|
|
|
|
/// Accepts a connection on the socket.
|
|
fn accept(&self) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "accept() is not supported");
|
|
}
|
|
|
|
/// Shuts down part of a full-duplex connection.
|
|
fn shutdown(&self, _cmd: SockShutdownCmd) -> Result<()> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "shutdown() is not supported");
|
|
}
|
|
|
|
/// Gets the address of this socket.
|
|
fn addr(&self) -> Result<SocketAddr> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "getsockname() is not supported");
|
|
}
|
|
|
|
/// Gets the address of the peer socket.
|
|
fn peer_addr(&self) -> Result<SocketAddr> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "getpeername() is not supported");
|
|
}
|
|
|
|
/// Gets options on the socket.
|
|
///
|
|
/// If the method succeeds, the result will be stored in the `option` parameter.
|
|
fn get_option(&self, _option: &mut dyn SocketOption) -> Result<()> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "getsockopt() is not supported");
|
|
}
|
|
|
|
/// Sets options on the socket.
|
|
fn set_option(&self, _option: &dyn SocketOption) -> Result<()> {
|
|
return_errno_with_message!(Errno::EOPNOTSUPP, "setsockopt() is not supported");
|
|
}
|
|
|
|
/// Sends a message on the socket.
|
|
fn sendmsg(
|
|
&self,
|
|
reader: &mut dyn MultiRead,
|
|
message_header: MessageHeader,
|
|
flags: SendRecvFlags,
|
|
) -> Result<usize>;
|
|
|
|
/// Receives a message from the socket.
|
|
///
|
|
/// If successful, the `io_vecs` buffer will be filled with the received content.
|
|
/// This method returns the length of the received message,
|
|
/// and the message header.
|
|
fn recvmsg(
|
|
&self,
|
|
writer: &mut dyn MultiWrite,
|
|
flags: SendRecvFlags,
|
|
) -> Result<(usize, MessageHeader)>;
|
|
|
|
/// Returns a reference to the pseudo inode associated with this socket.
|
|
fn pseudo_inode(&self) -> &Arc<dyn Inode>;
|
|
}
|
|
|
|
impl<T: Socket + 'static> FileLike for T {
|
|
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
|
|
if !writer.has_avail() {
|
|
// Linux always returns `Ok(0)` in this case, so we follow it.
|
|
return Ok(0);
|
|
}
|
|
|
|
// TODO: Set correct flags
|
|
self.recvmsg(writer, SendRecvFlags::empty())
|
|
.map(|(len, _)| len)
|
|
}
|
|
|
|
fn write(&self, reader: &mut VmReader) -> Result<usize> {
|
|
// TODO: Set correct flags
|
|
self.sendmsg(
|
|
reader,
|
|
MessageHeader::new(None, Vec::new()),
|
|
SendRecvFlags::empty(),
|
|
)
|
|
}
|
|
|
|
fn status_flags(&self) -> StatusFlags {
|
|
// TODO: Support other flags (e.g., `O_ASYNC`)
|
|
if self.is_nonblocking() {
|
|
StatusFlags::O_NONBLOCK
|
|
} else {
|
|
StatusFlags::empty()
|
|
}
|
|
}
|
|
|
|
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
|
// TODO: Support other flags (e.g., `O_ASYNC`)
|
|
if new_flags.contains(StatusFlags::O_NONBLOCK) {
|
|
self.set_nonblocking(true);
|
|
} else {
|
|
self.set_nonblocking(false);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn as_socket(&self) -> Option<&dyn Socket> {
|
|
Some(self)
|
|
}
|
|
|
|
fn inode(&self) -> &Arc<dyn Inode> {
|
|
self.pseudo_inode()
|
|
}
|
|
|
|
fn dump_proc_fdinfo(self: Arc<Self>, fd_flags: FdFlags) -> Box<dyn Display> {
|
|
struct FdInfo {
|
|
flags: u32,
|
|
ino: u64,
|
|
}
|
|
|
|
impl Display for FdInfo {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
writeln!(f, "pos:\t{}", 0)?;
|
|
writeln!(f, "flags:\t0{:o}", self.flags)?;
|
|
// TODO: This should be the mount ID of the pseudo filesystem.
|
|
writeln!(f, "mnt_id:\t{}", RESERVED_MOUNT_ID)?;
|
|
writeln!(f, "ino:\t{}", self.ino)
|
|
}
|
|
}
|
|
|
|
let mut flags = self.status_flags().bits() | self.access_mode() as u32;
|
|
if fd_flags.contains(FdFlags::CLOEXEC) {
|
|
flags |= CreationFlags::O_CLOEXEC.bits();
|
|
}
|
|
|
|
Box::new(FdInfo {
|
|
flags,
|
|
ino: self.inode().ino(),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Creates a new pseudo inode for a socket.
|
|
fn new_pseudo_inode() -> Arc<dyn Inode> {
|
|
Arc::new(PseudoInode::new(
|
|
0,
|
|
InodeType::Socket,
|
|
mkmod!(a+rwx),
|
|
Uid::new_root(),
|
|
Gid::new_root(),
|
|
aster_block::BLOCK_SIZE,
|
|
Arc::downgrade(sockfs_singleton()),
|
|
))
|
|
}
|