diff --git a/netstack/src/scheme/icmp.rs b/netstack/src/scheme/icmp.rs index 74cca1d071..9854a4df4e 100644 --- a/netstack/src/scheme/icmp.rs +++ b/netstack/src/scheme/icmp.rs @@ -171,7 +171,9 @@ impl<'a> SchemeSocket for IcmpSocket<'a> { file: &mut SocketFile, buf: &[u8], ) -> SyscallResult { - if self.can_send() { + if !file.write_enabled { + return Err(SyscallError::new(syscall::EPIPE)); + } else if self.can_send() { match file.data.socket_type { IcmpSocketType::Echo => { if buf.len() < mem::size_of::() { @@ -207,6 +209,9 @@ impl<'a> SchemeSocket for IcmpSocket<'a> { file: &mut SocketFile, buf: &mut [u8], ) -> SyscallResult { + if !file.read_enabled { + return Ok(0); + } while self.can_recv() { let (payload, _) = self.recv().expect("Can't recv icmp packet"); let icmp_packet = Icmpv4Packet::new_unchecked(&payload); @@ -283,4 +288,22 @@ impl<'a> SchemeSocket for IcmpSocket<'a> { ) -> SyscallResult { self.fpath(file, buf) } + + fn handle_shutdown(&mut self, file: &mut SchemeFile, how: usize) -> SyscallResult { + let socket_file = match file { + SchemeFile::Socket(ref mut file) => file, + _ => return Err(SyscallError::new(syscall::EBADF)), + }; + + match how { + 0 => socket_file.read_enabled = false, // SHUT_RD + 1 => socket_file.write_enabled = false, // SHUT_WR + 2 => { + socket_file.read_enabled = false; + socket_file.write_enabled = false; + } // SHUT_RDWR + _ => return Err(SyscallError::new(syscall::EINVAL)), + } + Ok(0) + } } diff --git a/netstack/src/scheme/ip.rs b/netstack/src/scheme/ip.rs index dbcc8efd64..21b83aedd4 100644 --- a/netstack/src/scheme/ip.rs +++ b/netstack/src/scheme/ip.rs @@ -98,7 +98,9 @@ impl<'a> SchemeSocket for RawSocket<'a> { file: &mut SocketFile, buf: &[u8], ) -> SyscallResult { - if self.can_send() { + if !file.write_enabled { + return Err(SyscallError::new(syscall::EPIPE)); + } else if self.can_send() { self.send_slice(buf).expect("Can't send slice"); Ok(buf.len()) } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { @@ -113,7 +115,9 @@ impl<'a> SchemeSocket for RawSocket<'a> { file: &mut SocketFile, buf: &mut [u8], ) -> SyscallResult { - if self.can_recv() { + if !file.read_enabled { + Ok(0) + } else if self.can_recv() { let length = self.recv_slice(buf).expect("Can't receive slice"); Ok(length) } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { @@ -152,4 +156,22 @@ impl<'a> SchemeSocket for RawSocket<'a> { ) -> SyscallResult { self.fpath(file, buf) } + + fn handle_shutdown(&mut self, file: &mut SchemeFile, how: usize) -> SyscallResult { + let socket_file = match file { + SchemeFile::Socket(ref mut file) => file, + _ => return Err(SyscallError::new(syscall::EBADF)), + }; + + match how { + 0 => socket_file.read_enabled = false, // SHUT_RD + 1 => socket_file.write_enabled = false, // SHUT_WR + 2 => { + socket_file.read_enabled = false; + socket_file.write_enabled = false; + } // SHUT_RDWR + _ => return Err(SyscallError::new(syscall::EINVAL)), + } + Ok(0) + } } diff --git a/netstack/src/scheme/socket.rs b/netstack/src/scheme/socket.rs index 6f7299d454..842dd0bd53 100644 --- a/netstack/src/scheme/socket.rs +++ b/netstack/src/scheme/socket.rs @@ -39,6 +39,8 @@ pub struct NullFile { pub flags: usize, pub uid: u32, pub gid: u32, + pub read_enabled: bool, + pub write_enabled: bool, } pub struct SocketFile { @@ -51,6 +53,8 @@ pub struct SocketFile { write_notified: bool, read_timeout: Option, write_timeout: Option, + pub read_enabled: bool, + pub write_enabled: bool, } impl SocketFile { @@ -63,6 +67,8 @@ impl SocketFile { read_timeout: self.read_timeout, write_timeout: self.write_timeout, socket_handle: self.socket_handle, + read_enabled: self.read_enabled, + write_enabled: self.write_enabled, data, } } @@ -75,6 +81,8 @@ impl SocketFile { write_notified: false, read_timeout: None, write_timeout: None, + read_enabled: true, + write_enabled: true, socket_handle, data, } @@ -226,6 +234,8 @@ where file: &SchemeFile, payload: &mut [u8], ) -> SyscallResult; + + fn handle_shutdown(&mut self, file: &mut SchemeFile, how: usize) -> SyscallResult; } pub struct SocketScheme @@ -442,6 +452,32 @@ where SocketT::handle_get_peer_name(socket, file, payload) } + SocketCall::Shutdown => { + let how = metadata[1] as usize; + + if let Some(null_file) = self.nulls.get_mut(&fd) { + match how { + 0 => null_file.read_enabled = false, + 1 => null_file.write_enabled = false, + 2 => { + null_file.read_enabled = false; + null_file.write_enabled = false; + } + _ => return Err(SyscallError::new(EINVAL)), + } + return Ok(0); + } + + let file = self + .files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + let mut socket_set = self.socket_set.borrow_mut(); + let socket = socket_set.get_mut::(file.socket_handle()); + + SocketT::handle_shutdown(socket, file, how) + } _ => Err(SyscallError::new(EOPNOTSUPP)), } } @@ -452,9 +488,17 @@ where flags: usize, uid: u32, gid: u32, + read_enabled: bool, + write_enabled: bool, ) -> SyscallResult { if path.is_empty() { - let null = NullFile { flags, uid, gid }; + let null = NullFile { + flags, + uid, + gid, + read_enabled, + write_enabled, + }; let id = self.next_fd; self.next_fd += 1; @@ -482,6 +526,8 @@ where write_notified: false, write_timeout: None, read_timeout: None, + read_enabled: read_enabled, + write_enabled: write_enabled, data, }); @@ -504,7 +550,7 @@ where SocketT: SchemeSocket + AnySocket<'static>, { fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> SyscallResult { - self.open_inner(path, flags, ctx.uid, ctx.gid) + self.open_inner(path, flags, ctx.uid, ctx.gid, true, true) } fn call( @@ -642,13 +688,19 @@ where fn dup(&mut self, fd: usize, buf: &[u8], _ctx: &CallerCtx) -> SyscallResult { let path = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; - if let Some((flags, uid, gid)) = self - .nulls - .get(&fd) - .map(|null| (null.flags, null.uid, null.gid)) + if let Some((flags, uid, gid, read_enabled, write_enabled)) = + self.nulls.get(&fd).map(|null| { + ( + null.flags, + null.uid, + null.gid, + null.read_enabled, + null.write_enabled, + ) + }) { // dup from empty path to a new path - return self.open_inner(path, flags, uid, gid); + return self.open_inner(path, flags, uid, gid, read_enabled, write_enabled); } let new_file = { diff --git a/netstack/src/scheme/tcp.rs b/netstack/src/scheme/tcp.rs index 62345a848e..1fbb7dc22c 100644 --- a/netstack/src/scheme/tcp.rs +++ b/netstack/src/scheme/tcp.rs @@ -153,7 +153,9 @@ impl<'a> SchemeSocket for TcpSocket<'a> { file: &mut SocketFile, buf: &[u8], ) -> SyscallResult { - if !self.is_active() { + if !file.write_enabled { + return Err(SyscallError::new(syscall::EPIPE)); + } else if !self.is_active() { Err(SyscallError::new(syscall::ENOTCONN)) } else if self.can_send() { self.send_slice(buf).expect("Can't send slice"); @@ -170,7 +172,9 @@ impl<'a> SchemeSocket for TcpSocket<'a> { file: &mut SocketFile, buf: &mut [u8], ) -> SyscallResult { - if !self.is_active() { + if !file.read_enabled { + Ok(0) + } else if !self.is_active() { Err(SyscallError::new(syscall::ENOTCONN)) } else if self.can_recv() { let length = self.recv_slice(buf).expect("Can't receive slice"); @@ -308,4 +312,26 @@ impl<'a> SchemeSocket for TcpSocket<'a> { ) -> SyscallResult { self.fpath(file, buf) } + + fn handle_shutdown(&mut self, file: &mut SchemeFile, how: usize) -> SyscallResult { + let socket_file = match file { + SchemeFile::Socket(ref mut file) => file, + _ => return Err(SyscallError::new(syscall::EBADF)), + }; + + match how { + 0 => socket_file.read_enabled = false, // SHUT_RD + 1 => { + socket_file.write_enabled = false; + self.close(); + } // SHUT_WR + 2 => { + socket_file.read_enabled = false; + socket_file.write_enabled = false; + self.close(); + } // SHUT_RDWR + _ => return Err(SyscallError::new(syscall::EINVAL)), + } + Ok(0) + } } diff --git a/netstack/src/scheme/udp.rs b/netstack/src/scheme/udp.rs index d5d99aa9e7..f1a7d46d77 100644 --- a/netstack/src/scheme/udp.rs +++ b/netstack/src/scheme/udp.rs @@ -144,6 +144,9 @@ impl<'a> SchemeSocket for UdpSocket<'a> { if !file.data.is_specified() { return Err(SyscallError::new(syscall::EADDRNOTAVAIL)); } + if !file.write_enabled { + return Err(SyscallError::new(syscall::EPIPE)); + } if self.can_send() { let endpoint = file.data; let endpoint = IpEndpoint::new( @@ -166,7 +169,9 @@ impl<'a> SchemeSocket for UdpSocket<'a> { file: &mut SocketFile, buf: &mut [u8], ) -> SyscallResult { - if self.can_recv() { + if !file.read_enabled { + Ok(0) + } else if self.can_recv() { let (length, _) = self.recv_slice(buf).expect("Can't receive slice"); Ok(length) } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { @@ -257,4 +262,22 @@ impl<'a> SchemeSocket for UdpSocket<'a> { ) -> SyscallResult { self.fpath(file, buf) } + + fn handle_shutdown(&mut self, file: &mut SchemeFile, how: usize) -> SyscallResult { + let socket_file = match file { + SchemeFile::Socket(ref mut file) => file, + _ => return Err(SyscallError::new(syscall::EBADF)), + }; + + match how { + 0 => socket_file.read_enabled = false, // SHUT_RD + 1 => socket_file.write_enabled = false, // SHUT_WR + 2 => { + socket_file.read_enabled = false; + socket_file.write_enabled = false; + } // SHUT_RDWR + _ => return Err(SyscallError::new(syscall::EINVAL)), + } + Ok(0) + } }