From 2b0b7cc5a478e0081c418118264f9fb9f34dbc8b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 8 Feb 2020 00:52:33 +0100 Subject: [PATCH] Net: Add a basic sys$shutdown() implementation Calling shutdown prevents further reads and/or writes on a socket. We should do a few more things based on the type of socket, but this initial implementation just puts the basic mechanism in place. Work towards #428. --- Kernel/Net/Socket.cpp | 13 +++++++++++++ Kernel/Net/Socket.h | 7 +++++++ Kernel/Process.cpp | 23 ++++++++++++++++++++++- Kernel/Process.h | 1 + Kernel/Syscall.h | 3 ++- Kernel/UnixTypes.h | 4 ++++ Libraries/LibC/sys/socket.cpp | 6 ++++++ Libraries/LibC/sys/socket.h | 5 +++++ 8 files changed, 60 insertions(+), 2 deletions(-) diff --git a/Kernel/Net/Socket.cpp b/Kernel/Net/Socket.cpp index 48acfe334a8..6e66db59c78 100644 --- a/Kernel/Net/Socket.cpp +++ b/Kernel/Net/Socket.cpp @@ -149,10 +149,23 @@ KResult Socket::getsockopt(FileDescription&, int level, int option, void* value, ssize_t Socket::read(FileDescription& description, u8* buffer, ssize_t size) { + if (is_shut_down_for_reading()) + return 0; return recvfrom(description, buffer, size, 0, nullptr, 0); } ssize_t Socket::write(FileDescription& description, const u8* data, ssize_t size) { + if (is_shut_down_for_writing()) + return -EPIPE; return sendto(description, data, size, 0, nullptr, 0); } + +KResult Socket::shutdown(int how) +{ + if (type() == SOCK_STREAM && !is_connected()) + return KResult(-ENOTCONN); + m_shut_down_for_reading |= how & SHUT_RD; + m_shut_down_for_writing |= how & SHUT_WR; + return KSuccess; +} diff --git a/Kernel/Net/Socket.h b/Kernel/Net/Socket.h index 7222d170f56..a0a7e1d2097 100644 --- a/Kernel/Net/Socket.h +++ b/Kernel/Net/Socket.h @@ -51,6 +51,9 @@ public: int type() const { return m_type; } int protocol() const { return m_protocol; } + bool is_shut_down_for_writing() const { return m_shut_down_for_writing; } + bool is_shut_down_for_reading() const { return m_shut_down_for_reading; } + enum class SetupState { Unstarted, // we haven't tried to set the socket up yet InProgress, // we're in the process of setting things up - for TCP maybe we've sent a SYN packet @@ -90,6 +93,8 @@ public: bool can_accept() const { return !m_pending.is_empty(); } RefPtr accept(); + KResult shutdown(int how); + virtual KResult bind(const sockaddr*, socklen_t) = 0; virtual KResult connect(FileDescription&, const sockaddr*, socklen_t, ShouldBlock) = 0; virtual KResult listen(int) = 0; @@ -153,6 +158,8 @@ private: int m_backlog { 0 }; SetupState m_setup_state { SetupState::Unstarted }; bool m_connected { false }; + bool m_shut_down_for_reading { false }; + bool m_shut_down_for_writing { false }; timeval m_receive_timeout { 0, 0 }; timeval m_send_timeout { 0, 0 }; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index d7ac924f8f2..3c3f975cabd 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -3221,6 +3221,22 @@ int Process::sys$connect(int sockfd, const sockaddr* address, socklen_t address_ return socket.connect(*description, address, address_size, description->is_blocking() ? ShouldBlock::Yes : ShouldBlock::No); } +int Process::sys$shutdown(int sockfd, int how) +{ + REQUIRE_PROMISE(stdio); + if (how & ~SHUT_RDWR) + return -EINVAL; + auto description = file_description(sockfd); + if (!description) + return -EBADF; + if (!description->is_socket()) + return -ENOTSOCK; + + auto& socket = *description->socket(); + REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain()); + return socket.shutdown(how); +} + ssize_t Process::sys$sendto(const Syscall::SC_sendto_params* user_params) { REQUIRE_PROMISE(stdio); @@ -3241,8 +3257,10 @@ ssize_t Process::sys$sendto(const Syscall::SC_sendto_params* user_params) return -EBADF; if (!description->is_socket()) return -ENOTSOCK; - SmapDisabler disabler; auto& socket = *description->socket(); + if (socket.is_shut_down_for_writing()) + return -EPIPE; + SmapDisabler disabler; return socket.sendto(*description, params.data.data, params.data.size, flags, addr, addr_length); } @@ -3276,6 +3294,9 @@ ssize_t Process::sys$recvfrom(const Syscall::SC_recvfrom_params* user_params) return -ENOTSOCK; auto& socket = *description->socket(); + if (socket.is_shut_down_for_reading()) + return 0; + bool original_blocking = description->is_blocking(); if (flags & MSG_DONTWAIT) description->set_blocking(false); diff --git a/Kernel/Process.h b/Kernel/Process.h index 5085aa4e5e7..e9156e42096 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -260,6 +260,7 @@ public: int sys$listen(int sockfd, int backlog); int sys$accept(int sockfd, sockaddr*, socklen_t*); int sys$connect(int sockfd, const sockaddr*, socklen_t); + int sys$shutdown(int sockfd, int how); ssize_t sys$sendto(const Syscall::SC_sendto_params*); ssize_t sys$recvfrom(const Syscall::SC_recvfrom_params*); int sys$getsockopt(const Syscall::SC_getsockopt_params*); diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 27dfb1dc7f2..41392023da3 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -177,7 +177,8 @@ typedef u32 socklen_t; __ENUMERATE_SYSCALL(chroot) \ __ENUMERATE_SYSCALL(pledge) \ __ENUMERATE_SYSCALL(unveil) \ - __ENUMERATE_SYSCALL(perf_event) + __ENUMERATE_SYSCALL(perf_event) \ + __ENUMERATE_SYSCALL(shutdown) namespace Syscall { diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index b7b83c5b78e..bb77f506c6c 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -387,6 +387,10 @@ struct pollfd { #define SOCK_NONBLOCK 04000 #define SOCK_CLOEXEC 02000000 +#define SHUT_RD 1 +#define SHUT_WR 2 +#define SHUT_RDWR 3 + #define MSG_DONTWAIT 0x40 #define SOL_SOCKET 1 diff --git a/Libraries/LibC/sys/socket.cpp b/Libraries/LibC/sys/socket.cpp index 41400db423e..b604c688c2d 100644 --- a/Libraries/LibC/sys/socket.cpp +++ b/Libraries/LibC/sys/socket.cpp @@ -62,6 +62,12 @@ int connect(int sockfd, const sockaddr* addr, socklen_t addrlen) __RETURN_WITH_ERRNO(rc, rc, -1); } +int shutdown(int sockfd, int how) +{ + int rc = syscall(SC_shutdown, sockfd, how); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + ssize_t sendto(int sockfd, const void* data, size_t data_length, int flags, const struct sockaddr* addr, socklen_t addr_length) { Syscall::SC_sendto_params params { sockfd, { data, data_length }, flags, addr, addr_length }; diff --git a/Libraries/LibC/sys/socket.h b/Libraries/LibC/sys/socket.h index a6558247616..9c9dced6560 100644 --- a/Libraries/LibC/sys/socket.h +++ b/Libraries/LibC/sys/socket.h @@ -49,6 +49,10 @@ __BEGIN_DECLS #define SOCK_NONBLOCK 04000 #define SOCK_CLOEXEC 02000000 +#define SHUT_RD 1 +#define SHUT_WR 2 +#define SHUT_RDWR 3 + #define IPPROTO_IP 0 #define IPPROTO_ICMP 1 #define IPPROTO_TCP 6 @@ -81,6 +85,7 @@ int bind(int sockfd, const struct sockaddr* addr, socklen_t); int listen(int sockfd, int backlog); int accept(int sockfd, struct sockaddr*, socklen_t*); int connect(int sockfd, const struct sockaddr*, socklen_t); +int shutdown(int sockfd, int how); ssize_t send(int sockfd, const void*, size_t, int flags); ssize_t sendto(int sockfd, const void*, size_t, int flags, const struct sockaddr*, socklen_t); ssize_t recv(int sockfd, void*, size_t, int flags);