1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 01:47:36 +00:00

Kernel: Add so_error to keep track of the socket's error state

This sets the m_so_error variable every time the socket returns an
error.
This commit is contained in:
brapru 2021-08-01 11:27:23 -04:00 committed by Andreas Kling
parent f424485193
commit 0095c7cb7d
6 changed files with 88 additions and 78 deletions

View file

@ -108,20 +108,20 @@ KResult IPv4Socket::bind(Userspace<const sockaddr*> user_address, socklen_t addr
{ {
VERIFY(setup_state() == SetupState::Unstarted); VERIFY(setup_state() == SetupState::Unstarted);
if (address_size != sizeof(sockaddr_in)) if (address_size != sizeof(sockaddr_in))
return EINVAL; return set_so_error(EINVAL);
sockaddr_in address; sockaddr_in address;
if (!copy_from_user(&address, user_address, sizeof(sockaddr_in))) if (!copy_from_user(&address, user_address, sizeof(sockaddr_in)))
return EFAULT; return set_so_error(EFAULT);
if (address.sin_family != AF_INET) if (address.sin_family != AF_INET)
return EINVAL; return set_so_error(EINVAL);
auto requested_local_port = ntohs(address.sin_port); auto requested_local_port = ntohs(address.sin_port);
if (!Process::current()->is_superuser()) { if (!Process::current()->is_superuser()) {
if (requested_local_port > 0 && requested_local_port < 1024) { if (requested_local_port > 0 && requested_local_port < 1024) {
dbgln("UID {} attempted to bind {} to port {}", Process::current()->uid(), class_name(), requested_local_port); dbgln("UID {} attempted to bind {} to port {}", Process::current()->uid(), class_name(), requested_local_port);
return EACCES; return set_so_error(EACCES);
} }
} }
@ -152,19 +152,19 @@ KResult IPv4Socket::listen(size_t backlog)
KResult IPv4Socket::connect(FileDescription& description, Userspace<const sockaddr*> address, socklen_t address_size, ShouldBlock should_block) KResult IPv4Socket::connect(FileDescription& description, Userspace<const sockaddr*> address, socklen_t address_size, ShouldBlock should_block)
{ {
if (address_size != sizeof(sockaddr_in)) if (address_size != sizeof(sockaddr_in))
return EINVAL; return set_so_error(EINVAL);
u16 sa_family_copy; u16 sa_family_copy;
auto* user_address = reinterpret_cast<const sockaddr*>(address.unsafe_userspace_ptr()); auto* user_address = reinterpret_cast<const sockaddr*>(address.unsafe_userspace_ptr());
if (!copy_from_user(&sa_family_copy, &user_address->sa_family, sizeof(u16))) if (!copy_from_user(&sa_family_copy, &user_address->sa_family, sizeof(u16)))
return EFAULT; return set_so_error(EFAULT);
if (sa_family_copy != AF_INET) if (sa_family_copy != AF_INET)
return EINVAL; return set_so_error(EINVAL);
if (m_role == Role::Connected) if (m_role == Role::Connected)
return EISCONN; return set_so_error(EISCONN);
sockaddr_in safe_address; sockaddr_in safe_address;
if (!copy_from_user(&safe_address, (const sockaddr_in*)user_address, sizeof(sockaddr_in))) if (!copy_from_user(&safe_address, (const sockaddr_in*)user_address, sizeof(sockaddr_in)))
return EFAULT; return set_so_error(EFAULT);
m_peer_address = IPv4Address((const u8*)&safe_address.sin_addr.s_addr); m_peer_address = IPv4Address((const u8*)&safe_address.sin_addr.s_addr);
if (m_peer_address == IPv4Address { 0, 0, 0, 0 }) if (m_peer_address == IPv4Address { 0, 0, 0, 0 })
@ -205,16 +205,16 @@ KResultOr<size_t> IPv4Socket::sendto(FileDescription&, const UserOrKernelBuffer&
MutexLocker locker(lock()); MutexLocker locker(lock());
if (addr && addr_length != sizeof(sockaddr_in)) if (addr && addr_length != sizeof(sockaddr_in))
return EINVAL; return set_so_error(EINVAL);
if (addr) { if (addr) {
sockaddr_in ia; sockaddr_in ia;
if (!copy_from_user(&ia, Userspace<const sockaddr_in*>(addr.ptr()))) if (!copy_from_user(&ia, Userspace<const sockaddr_in*>(addr.ptr())))
return EFAULT; return set_so_error(EFAULT);
if (ia.sin_family != AF_INET) { if (ia.sin_family != AF_INET) {
dmesgln("sendto: Bad address family: {} is not AF_INET", ia.sin_family); dmesgln("sendto: Bad address family: {} is not AF_INET", ia.sin_family);
return EAFNOSUPPORT; return set_so_error(EAFNOSUPPORT);
} }
m_peer_address = IPv4Address((const u8*)&ia.sin_addr.s_addr); m_peer_address = IPv4Address((const u8*)&ia.sin_addr.s_addr);
@ -222,11 +222,11 @@ KResultOr<size_t> IPv4Socket::sendto(FileDescription&, const UserOrKernelBuffer&
} }
if (!is_connected() && m_peer_address.is_zero()) if (!is_connected() && m_peer_address.is_zero())
return EPIPE; return set_so_error(EPIPE);
auto routing_decision = route_to(m_peer_address, m_local_address, bound_interface()); auto routing_decision = route_to(m_peer_address, m_local_address, bound_interface());
if (routing_decision.is_zero()) if (routing_decision.is_zero())
return EHOSTUNREACH; return set_so_error(EHOSTUNREACH);
if (m_local_address.to_u32() == 0) if (m_local_address.to_u32() == 0)
m_local_address = routing_decision.adapter->ipv4_address(); m_local_address = routing_decision.adapter->ipv4_address();
@ -241,12 +241,12 @@ KResultOr<size_t> IPv4Socket::sendto(FileDescription&, const UserOrKernelBuffer&
data_length = min(data_length, routing_decision.adapter->mtu() - ipv4_payload_offset); data_length = min(data_length, routing_decision.adapter->mtu() - ipv4_payload_offset);
auto packet = routing_decision.adapter->acquire_packet_buffer(ipv4_payload_offset + data_length); auto packet = routing_decision.adapter->acquire_packet_buffer(ipv4_payload_offset + data_length);
if (!packet) if (!packet)
return ENOMEM; return set_so_error(ENOMEM);
routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), routing_decision.next_hop, routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), routing_decision.next_hop,
m_peer_address, (IPv4Protocol)protocol(), data_length, m_ttl); m_peer_address, (IPv4Protocol)protocol(), data_length, m_ttl);
if (!data.read(packet->buffer->data() + ipv4_payload_offset, data_length)) { if (!data.read(packet->buffer->data() + ipv4_payload_offset, data_length)) {
routing_decision.adapter->release_packet_buffer(*packet); routing_decision.adapter->release_packet_buffer(*packet);
return EFAULT; return set_so_error(EFAULT);
} }
routing_decision.adapter->send_packet(packet->bytes()); routing_decision.adapter->send_packet(packet->bytes());
routing_decision.adapter->release_packet_buffer(*packet); routing_decision.adapter->release_packet_buffer(*packet);
@ -266,7 +266,7 @@ KResultOr<size_t> IPv4Socket::receive_byte_buffered(FileDescription& description
if (protocol_is_disconnected()) if (protocol_is_disconnected())
return 0; return 0;
if (!description.is_blocking()) if (!description.is_blocking())
return EAGAIN; return set_so_error(EAGAIN);
locker.unlock(); locker.unlock();
auto unblocked_flags = BlockFlags::None; auto unblocked_flags = BlockFlags::None;
@ -275,10 +275,10 @@ KResultOr<size_t> IPv4Socket::receive_byte_buffered(FileDescription& description
if (!has_flag(unblocked_flags, BlockFlags::Read)) { if (!has_flag(unblocked_flags, BlockFlags::Read)) {
if (res.was_interrupted()) if (res.was_interrupted())
return EINTR; return set_so_error(EINTR);
// Unblocked due to timeout. // Unblocked due to timeout.
return EAGAIN; return set_so_error(EAGAIN);
} }
} }
@ -306,7 +306,7 @@ KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& descripti
if (protocol_is_disconnected()) if (protocol_is_disconnected())
return 0; return 0;
if (!description.is_blocking()) if (!description.is_blocking())
return EAGAIN; return set_so_error(EAGAIN);
} }
if (!m_receive_queue.is_empty()) { if (!m_receive_queue.is_empty()) {
@ -336,10 +336,10 @@ KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& descripti
if (!has_flag(unblocked_flags, BlockFlags::Read)) { if (!has_flag(unblocked_flags, BlockFlags::Read)) {
if (res.was_interrupted()) if (res.was_interrupted())
return EINTR; return set_so_error(EINTR);
// Unblocked due to timeout. // Unblocked due to timeout.
return EAGAIN; return set_so_error(EAGAIN);
} }
VERIFY(m_can_read); VERIFY(m_can_read);
VERIFY(!m_receive_queue.is_empty()); VERIFY(!m_receive_queue.is_empty());
@ -369,18 +369,18 @@ KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& descripti
out_addr.sin_family = AF_INET; out_addr.sin_family = AF_INET;
Userspace<sockaddr_in*> dest_addr = addr.ptr(); Userspace<sockaddr_in*> dest_addr = addr.ptr();
if (!copy_to_user(dest_addr, &out_addr)) if (!copy_to_user(dest_addr, &out_addr))
return EFAULT; return set_so_error(EFAULT);
socklen_t out_length = sizeof(sockaddr_in); socklen_t out_length = sizeof(sockaddr_in);
VERIFY(addr_length); VERIFY(addr_length);
if (!copy_to_user(addr_length, &out_length)) if (!copy_to_user(addr_length, &out_length))
return EFAULT; return set_so_error(EFAULT);
} }
if (type() == SOCK_RAW) { if (type() == SOCK_RAW) {
size_t bytes_written = min(packet.data.value().size(), buffer_length); size_t bytes_written = min(packet.data.value().size(), buffer_length);
if (!buffer.write(packet.data.value().data(), bytes_written)) if (!buffer.write(packet.data.value().data(), bytes_written))
return EFAULT; return set_so_error(EFAULT);
return bytes_written; return bytes_written;
} }
@ -392,9 +392,9 @@ KResultOr<size_t> IPv4Socket::recvfrom(FileDescription& description, UserOrKerne
if (user_addr_length) { if (user_addr_length) {
socklen_t addr_length; socklen_t addr_length;
if (!copy_from_user(&addr_length, user_addr_length.unsafe_userspace_ptr())) if (!copy_from_user(&addr_length, user_addr_length.unsafe_userspace_ptr()))
return EFAULT; return set_so_error(EFAULT);
if (addr_length < sizeof(sockaddr_in)) if (addr_length < sizeof(sockaddr_in))
return EINVAL; return set_so_error(EINVAL);
} }
dbgln_if(IPV4_SOCKET_DEBUG, "recvfrom: type={}, local_port={}", type(), local_port()); dbgln_if(IPV4_SOCKET_DEBUG, "recvfrom: type={}, local_port={}", type(), local_port());

View file

@ -122,14 +122,14 @@ KResult LocalSocket::bind(Userspace<const sockaddr*> user_address, socklen_t add
{ {
VERIFY(setup_state() == SetupState::Unstarted); VERIFY(setup_state() == SetupState::Unstarted);
if (address_size != sizeof(sockaddr_un)) if (address_size != sizeof(sockaddr_un))
return EINVAL; return set_so_error(EINVAL);
sockaddr_un address; sockaddr_un address;
if (!copy_from_user(&address, user_address, sizeof(sockaddr_un))) if (!copy_from_user(&address, user_address, sizeof(sockaddr_un)))
return EFAULT; return set_so_error(EFAULT);
if (address.sun_family != AF_LOCAL) if (address.sun_family != AF_LOCAL)
return EINVAL; return set_so_error(EINVAL);
auto path = String(address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path))); auto path = String(address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path)));
@ -140,7 +140,7 @@ KResult LocalSocket::bind(Userspace<const sockaddr*> user_address, socklen_t add
auto result = VirtualFileSystem::the().open(path, O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR, mode, Process::current()->current_directory(), owner); auto result = VirtualFileSystem::the().open(path, O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR, mode, Process::current()->current_directory(), owner);
if (result.is_error()) { if (result.is_error()) {
if (result.error() == -EEXIST) if (result.error() == -EEXIST)
return EADDRINUSE; return set_so_error(EADDRINUSE);
return result.error(); return result.error();
} }
@ -148,7 +148,7 @@ KResult LocalSocket::bind(Userspace<const sockaddr*> user_address, socklen_t add
VERIFY(file->inode()); VERIFY(file->inode());
if (!file->inode()->bind_socket(*this)) if (!file->inode()->bind_socket(*this))
return EADDRINUSE; return set_so_error(EADDRINUSE);
m_file = move(file); m_file = move(file);
@ -161,33 +161,33 @@ KResult LocalSocket::connect(FileDescription& description, Userspace<const socka
{ {
VERIFY(!m_bound); VERIFY(!m_bound);
if (address_size != sizeof(sockaddr_un)) if (address_size != sizeof(sockaddr_un))
return EINVAL; return set_so_error(EINVAL);
u16 sa_family_copy; u16 sa_family_copy;
auto* user_address = reinterpret_cast<const sockaddr*>(address.unsafe_userspace_ptr()); auto* user_address = reinterpret_cast<const sockaddr*>(address.unsafe_userspace_ptr());
if (!copy_from_user(&sa_family_copy, &user_address->sa_family, sizeof(u16))) if (!copy_from_user(&sa_family_copy, &user_address->sa_family, sizeof(u16)))
return EFAULT; return set_so_error(EFAULT);
if (sa_family_copy != AF_LOCAL) if (sa_family_copy != AF_LOCAL)
return EINVAL; return set_so_error(EINVAL);
if (is_connected()) if (is_connected())
return EISCONN; return set_so_error(EISCONN);
const auto& local_address = *reinterpret_cast<const sockaddr_un*>(user_address); const auto& local_address = *reinterpret_cast<const sockaddr_un*>(user_address);
char safe_address[sizeof(local_address.sun_path) + 1] = { 0 }; char safe_address[sizeof(local_address.sun_path) + 1] = { 0 };
if (!copy_from_user(&safe_address[0], &local_address.sun_path[0], sizeof(safe_address) - 1)) if (!copy_from_user(&safe_address[0], &local_address.sun_path[0], sizeof(safe_address) - 1))
return EFAULT; return set_so_error(EFAULT);
safe_address[sizeof(safe_address) - 1] = '\0'; safe_address[sizeof(safe_address) - 1] = '\0';
dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({})", this, safe_address); dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({})", this, safe_address);
auto description_or_error = VirtualFileSystem::the().open(safe_address, O_RDWR, 0, Process::current()->current_directory()); auto description_or_error = VirtualFileSystem::the().open(safe_address, O_RDWR, 0, Process::current()->current_directory());
if (description_or_error.is_error()) if (description_or_error.is_error())
return ECONNREFUSED; return set_so_error(ECONNREFUSED);
m_file = move(description_or_error.value()); m_file = move(description_or_error.value());
VERIFY(m_file->inode()); VERIFY(m_file->inode());
if (!m_file->inode()->socket()) if (!m_file->inode()->socket())
return ECONNREFUSED; return set_so_error(ECONNREFUSED);
m_address.sun_family = sa_family_copy; m_address.sun_family = sa_family_copy;
memcpy(m_address.sun_path, safe_address, sizeof(m_address.sun_path)); memcpy(m_address.sun_path, safe_address, sizeof(m_address.sun_path));
@ -210,14 +210,14 @@ KResult LocalSocket::connect(FileDescription& description, Userspace<const socka
auto unblock_flags = Thread::FileDescriptionBlocker::BlockFlags::None; auto unblock_flags = Thread::FileDescriptionBlocker::BlockFlags::None;
if (Thread::current()->block<Thread::ConnectBlocker>({}, description, unblock_flags).was_interrupted()) { if (Thread::current()->block<Thread::ConnectBlocker>({}, description, unblock_flags).was_interrupted()) {
set_connect_side_role(Role::None); set_connect_side_role(Role::None);
return EINTR; return set_so_error(EINTR);
} }
dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({}) status is {}", this, safe_address, to_string(setup_state())); dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({}) status is {}", this, safe_address, to_string(setup_state()));
if (!has_flag(unblock_flags, Thread::FileDescriptionBlocker::BlockFlags::Connect)) { if (!has_flag(unblock_flags, Thread::FileDescriptionBlocker::BlockFlags::Connect)) {
set_connect_side_role(Role::None); set_connect_side_role(Role::None);
return ECONNREFUSED; return set_so_error(ECONNREFUSED);
} }
set_connect_side_role(Role::Connected); set_connect_side_role(Role::Connected);
return KSuccess; return KSuccess;
@ -227,7 +227,7 @@ KResult LocalSocket::listen(size_t backlog)
{ {
MutexLocker locker(lock()); MutexLocker locker(lock());
if (type() != SOCK_STREAM) if (type() != SOCK_STREAM)
return EOPNOTSUPP; return set_so_error(EOPNOTSUPP);
set_backlog(backlog); set_backlog(backlog);
auto previous_role = m_role; auto previous_role = m_role;
m_role = Role::Listener; m_role = Role::Listener;
@ -300,10 +300,10 @@ bool LocalSocket::can_write(const FileDescription& description, size_t) const
KResultOr<size_t> LocalSocket::sendto(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size, int, Userspace<const sockaddr*>, socklen_t) KResultOr<size_t> LocalSocket::sendto(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size, int, Userspace<const sockaddr*>, socklen_t)
{ {
if (!has_attached_peer(description)) if (!has_attached_peer(description))
return EPIPE; return set_so_error(EPIPE);
auto* socket_buffer = send_buffer_for(description); auto* socket_buffer = send_buffer_for(description);
if (!socket_buffer) if (!socket_buffer)
return EINVAL; return set_so_error(EINVAL);
auto nwritten_or_error = socket_buffer->write(data, data_size); auto nwritten_or_error = socket_buffer->write(data, data_size);
if (!nwritten_or_error.is_error() && nwritten_or_error.value() > 0) if (!nwritten_or_error.is_error() && nwritten_or_error.value() > 0)
Thread::current()->did_unix_socket_write(nwritten_or_error.value()); Thread::current()->did_unix_socket_write(nwritten_or_error.value());
@ -334,17 +334,18 @@ KResultOr<size_t> LocalSocket::recvfrom(FileDescription& description, UserOrKern
{ {
auto* socket_buffer = receive_buffer_for(description); auto* socket_buffer = receive_buffer_for(description);
if (!socket_buffer) if (!socket_buffer)
return EINVAL; return set_so_error(EINVAL);
if (!description.is_blocking()) { if (!description.is_blocking()) {
if (socket_buffer->is_empty()) { if (socket_buffer->is_empty()) {
if (!has_attached_peer(description)) if (!has_attached_peer(description))
return 0; return 0;
return EAGAIN; return set_so_error(EAGAIN);
} }
} else if (!can_read(description, 0)) { } else if (!can_read(description, 0)) {
auto unblock_flags = Thread::FileDescriptionBlocker::BlockFlags::None; auto unblock_flags = Thread::FileDescriptionBlocker::BlockFlags::None;
if (Thread::current()->block<Thread::ReadBlocker>({}, description, unblock_flags).was_interrupted()) if (Thread::current()->block<Thread::ReadBlocker>({}, description, unblock_flags).was_interrupted()) {
return EINTR; return set_so_error(EINTR);
}
} }
if (!has_attached_peer(description) && socket_buffer->is_empty()) if (!has_attached_peer(description) && socket_buffer->is_empty())
return 0; return 0;
@ -447,7 +448,7 @@ KResult LocalSocket::chown(FileDescription&, uid_t uid, gid_t gid)
auto current_process = Process::current(); auto current_process = Process::current();
if (!current_process->is_superuser() && (current_process->euid() != uid || !current_process->in_group(gid))) if (!current_process->is_superuser() && (current_process->euid() != uid || !current_process->in_group(gid)))
return EPERM; return set_so_error(EPERM);
m_prebind_uid = uid; m_prebind_uid = uid;
m_prebind_gid = gid; m_prebind_gid = gid;
@ -479,13 +480,13 @@ KResult LocalSocket::sendfd(const FileDescription& socket_description, FileDescr
MutexLocker locker(lock()); MutexLocker locker(lock());
auto role = this->role(socket_description); auto role = this->role(socket_description);
if (role != Role::Connected && role != Role::Accepted) if (role != Role::Connected && role != Role::Accepted)
return EINVAL; return set_so_error(EINVAL);
auto& queue = sendfd_queue_for(socket_description); auto& queue = sendfd_queue_for(socket_description);
// FIXME: Figure out how we should limit this properly. // FIXME: Figure out how we should limit this properly.
if (queue.size() > 128) if (queue.size() > 128)
return EBUSY; return set_so_error(EBUSY);
if (!queue.try_append(move(passing_description))) if (!queue.try_append(move(passing_description)))
return ENOMEM; return set_so_error(ENOMEM);
return KSuccess; return KSuccess;
} }
@ -494,11 +495,11 @@ KResultOr<NonnullRefPtr<FileDescription>> LocalSocket::recvfd(const FileDescript
MutexLocker locker(lock()); MutexLocker locker(lock());
auto role = this->role(socket_description); auto role = this->role(socket_description);
if (role != Role::Connected && role != Role::Accepted) if (role != Role::Connected && role != Role::Accepted)
return EINVAL; return set_so_error(EINVAL);
auto& queue = recvfd_queue_for(socket_description); auto& queue = recvfd_queue_for(socket_description);
if (queue.is_empty()) { if (queue.is_empty()) {
// FIXME: Figure out the perfect error code for this. // FIXME: Figure out the perfect error code for this.
return EAGAIN; return set_so_error(EAGAIN);
} }
return queue.take_first(); return queue.take_first();
} }

View file

@ -71,9 +71,9 @@ KResult Socket::queue_connection_from(NonnullRefPtr<Socket> peer)
dbgln_if(SOCKET_DEBUG, "Socket({}) queueing connection", this); dbgln_if(SOCKET_DEBUG, "Socket({}) queueing connection", this);
MutexLocker locker(m_lock); MutexLocker locker(m_lock);
if (m_pending.size() >= m_backlog) if (m_pending.size() >= m_backlog)
return ECONNREFUSED; return set_so_error(ECONNREFUSED);
if (!m_pending.try_append(peer)) if (!m_pending.try_append(peer))
return ENOMEM; return set_so_error(ENOMEM);
evaluate_block_conditions(); evaluate_block_conditions();
return KSuccess; return KSuccess;
} }
@ -235,7 +235,7 @@ KResultOr<size_t> Socket::read(FileDescription& description, u64, UserOrKernelBu
KResultOr<size_t> Socket::write(FileDescription& description, u64, const UserOrKernelBuffer& data, size_t size) KResultOr<size_t> Socket::write(FileDescription& description, u64, const UserOrKernelBuffer& data, size_t size)
{ {
if (is_shut_down_for_writing()) if (is_shut_down_for_writing())
return EPIPE; return set_so_error(EPIPE);
return sendto(description, data, size, 0, {}, 0); return sendto(description, data, size, 0, {}, 0);
} }
@ -243,9 +243,9 @@ KResult Socket::shutdown(int how)
{ {
MutexLocker locker(lock()); MutexLocker locker(lock());
if (type() == SOCK_STREAM && !is_connected()) if (type() == SOCK_STREAM && !is_connected())
return ENOTCONN; return set_so_error(ENOTCONN);
if (m_role == Role::Listener) if (m_role == Role::Listener)
return ENOTCONN; return set_so_error(ENOTCONN);
if (!m_shut_down_for_writing && (how & SHUT_WR)) if (!m_shut_down_for_writing && (how & SHUT_WR))
shut_down_for_writing(); shut_down_for_writing();
if (!m_shut_down_for_reading && (how & SHUT_RD)) if (!m_shut_down_for_reading && (how & SHUT_RD))

View file

@ -130,6 +130,13 @@ protected:
Role m_role { Role::None }; Role m_role { Role::None };
KResult so_error() const { return m_so_error; }
KResult set_so_error(KResult error)
{
m_so_error = error;
return error;
}
protected: protected:
ucred m_origin { 0, 0, 0 }; ucred m_origin { 0, 0, 0 };
ucred m_acceptor { 0, 0, 0 }; ucred m_acceptor { 0, 0, 0 };
@ -154,6 +161,8 @@ private:
Time m_send_timeout {}; Time m_send_timeout {};
int m_timestamp { 0 }; int m_timestamp { 0 };
KResult m_so_error { KSuccess };
NonnullRefPtrVector<Socket> m_pending; NonnullRefPtrVector<Socket> m_pending;
}; };

View file

@ -171,7 +171,7 @@ KResultOr<size_t> TCPSocket::protocol_receive(ReadonlyBytes raw_ipv4_packet, Use
dbgln_if(TCP_SOCKET_DEBUG, "payload_size {}, will it fit in {}?", payload_size, buffer_size); dbgln_if(TCP_SOCKET_DEBUG, "payload_size {}, will it fit in {}?", payload_size, buffer_size);
VERIFY(buffer_size >= payload_size); VERIFY(buffer_size >= payload_size);
if (!buffer.write(tcp_packet.payload(), payload_size)) if (!buffer.write(tcp_packet.payload(), payload_size))
return EFAULT; return set_so_error(EFAULT);
return payload_size; return payload_size;
} }
@ -179,7 +179,7 @@ KResultOr<size_t> TCPSocket::protocol_send(const UserOrKernelBuffer& data, size_
{ {
RoutingDecision routing_decision = route_to(peer_address(), local_address(), bound_interface()); RoutingDecision routing_decision = route_to(peer_address(), local_address(), bound_interface());
if (routing_decision.is_zero()) if (routing_decision.is_zero())
return EHOSTUNREACH; return set_so_error(EHOSTUNREACH);
size_t mss = routing_decision.adapter->mtu() - sizeof(IPv4Packet) - sizeof(TCPPacket); size_t mss = routing_decision.adapter->mtu() - sizeof(IPv4Packet) - sizeof(TCPPacket);
data_length = min(data_length, mss); data_length = min(data_length, mss);
int err = send_tcp_packet(TCPFlags::PUSH | TCPFlags::ACK, &data, data_length, &routing_decision); int err = send_tcp_packet(TCPFlags::PUSH | TCPFlags::ACK, &data, data_length, &routing_decision);
@ -199,7 +199,7 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload,
{ {
RoutingDecision routing_decision = user_routing_decision ? *user_routing_decision : route_to(peer_address(), local_address(), bound_interface()); RoutingDecision routing_decision = user_routing_decision ? *user_routing_decision : route_to(peer_address(), local_address(), bound_interface());
if (routing_decision.is_zero()) if (routing_decision.is_zero())
return EHOSTUNREACH; return set_so_error(EHOSTUNREACH);
auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset();
@ -209,7 +209,7 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload,
const size_t buffer_size = ipv4_payload_offset + tcp_header_size + payload_size; const size_t buffer_size = ipv4_payload_offset + tcp_header_size + payload_size;
auto packet = routing_decision.adapter->acquire_packet_buffer(buffer_size); auto packet = routing_decision.adapter->acquire_packet_buffer(buffer_size);
if (!packet) if (!packet)
return ENOMEM; return set_so_error(ENOMEM);
routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(),
routing_decision.next_hop, peer_address(), IPv4Protocol::TCP, routing_decision.next_hop, peer_address(), IPv4Protocol::TCP,
buffer_size - ipv4_payload_offset, ttl()); buffer_size - ipv4_payload_offset, ttl());
@ -231,7 +231,7 @@ KResult TCPSocket::send_tcp_packet(u16 flags, const UserOrKernelBuffer* payload,
if (payload && !payload->read(tcp_packet.payload(), payload_size)) { if (payload && !payload->read(tcp_packet.payload(), payload_size)) {
routing_decision.adapter->release_packet_buffer(*packet); routing_decision.adapter->release_packet_buffer(*packet);
return EFAULT; return set_so_error(EFAULT);
} }
if (flags & TCPFlags::SYN) { if (flags & TCPFlags::SYN) {
@ -370,7 +370,7 @@ KResult TCPSocket::protocol_bind()
if (has_specific_local_address() && !m_adapter) { if (has_specific_local_address() && !m_adapter) {
m_adapter = NetworkingManagement::the().from_ipv4_address(local_address()); m_adapter = NetworkingManagement::the().from_ipv4_address(local_address());
if (!m_adapter) if (!m_adapter)
return EADDRNOTAVAIL; return set_so_error(EADDRNOTAVAIL);
} }
return KSuccess; return KSuccess;
@ -386,7 +386,7 @@ KResult TCPSocket::protocol_listen(bool did_allocate_port)
return true; return true;
}); });
if (!ok) if (!ok)
return EADDRINUSE; return set_so_error(EADDRINUSE);
} }
set_direction(Direction::Passive); set_direction(Direction::Passive);
@ -401,7 +401,7 @@ KResult TCPSocket::protocol_connect(FileDescription& description, ShouldBlock sh
auto routing_decision = route_to(peer_address(), local_address()); auto routing_decision = route_to(peer_address(), local_address());
if (routing_decision.is_zero()) if (routing_decision.is_zero())
return EHOSTUNREACH; return set_so_error(EHOSTUNREACH);
if (!has_specific_local_address()) if (!has_specific_local_address())
set_local_address(routing_decision.adapter->ipv4_address()); set_local_address(routing_decision.adapter->ipv4_address());
@ -425,20 +425,20 @@ KResult TCPSocket::protocol_connect(FileDescription& description, ShouldBlock sh
locker.unlock(); locker.unlock();
auto unblock_flags = Thread::FileBlocker::BlockFlags::None; auto unblock_flags = Thread::FileBlocker::BlockFlags::None;
if (Thread::current()->block<Thread::ConnectBlocker>({}, description, unblock_flags).was_interrupted()) if (Thread::current()->block<Thread::ConnectBlocker>({}, description, unblock_flags).was_interrupted())
return EINTR; return set_so_error(EINTR);
locker.lock(); locker.lock();
VERIFY(setup_state() == SetupState::Completed); VERIFY(setup_state() == SetupState::Completed);
if (has_error()) { // TODO: check unblock_flags if (has_error()) { // TODO: check unblock_flags
m_role = Role::None; m_role = Role::None;
if (error() == TCPSocket::Error::RetransmitTimeout) if (error() == TCPSocket::Error::RetransmitTimeout)
return ETIMEDOUT; return set_so_error(ETIMEDOUT);
else else
return ECONNREFUSED; return set_so_error(ECONNREFUSED);
} }
return KSuccess; return KSuccess;
} }
return EINPROGRESS; return set_so_error(EINPROGRESS);
} }
KResultOr<u16> TCPSocket::protocol_allocate_local_port() KResultOr<u16> TCPSocket::protocol_allocate_local_port()
@ -464,7 +464,7 @@ KResultOr<u16> TCPSocket::protocol_allocate_local_port()
if (port == first_scan_port) if (port == first_scan_port)
break; break;
} }
return EADDRINUSE; return set_so_error(EADDRINUSE);
}); });
} }

View file

@ -69,7 +69,7 @@ KResultOr<size_t> UDPSocket::protocol_receive(ReadonlyBytes raw_ipv4_packet, Use
VERIFY(udp_packet.length() >= sizeof(UDPPacket)); // FIXME: This should be rejected earlier. VERIFY(udp_packet.length() >= sizeof(UDPPacket)); // FIXME: This should be rejected earlier.
size_t read_size = min(buffer_size, udp_packet.length() - sizeof(UDPPacket)); size_t read_size = min(buffer_size, udp_packet.length() - sizeof(UDPPacket));
if (!buffer.write(udp_packet.payload(), read_size)) if (!buffer.write(udp_packet.payload(), read_size))
return EFAULT; return set_so_error(EFAULT);
return read_size; return read_size;
} }
@ -77,20 +77,20 @@ KResultOr<size_t> UDPSocket::protocol_send(const UserOrKernelBuffer& data, size_
{ {
auto routing_decision = route_to(peer_address(), local_address(), bound_interface()); auto routing_decision = route_to(peer_address(), local_address(), bound_interface());
if (routing_decision.is_zero()) if (routing_decision.is_zero())
return EHOSTUNREACH; return set_so_error(EHOSTUNREACH);
auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset();
data_length = min(data_length, routing_decision.adapter->mtu() - ipv4_payload_offset - sizeof(UDPPacket)); data_length = min(data_length, routing_decision.adapter->mtu() - ipv4_payload_offset - sizeof(UDPPacket));
const size_t udp_buffer_size = sizeof(UDPPacket) + data_length; const size_t udp_buffer_size = sizeof(UDPPacket) + data_length;
auto packet = routing_decision.adapter->acquire_packet_buffer(ipv4_payload_offset + udp_buffer_size); auto packet = routing_decision.adapter->acquire_packet_buffer(ipv4_payload_offset + udp_buffer_size);
if (!packet) if (!packet)
return ENOMEM; return set_so_error(ENOMEM);
memset(packet->buffer->data() + ipv4_payload_offset, 0, sizeof(UDPPacket)); memset(packet->buffer->data() + ipv4_payload_offset, 0, sizeof(UDPPacket));
auto& udp_packet = *reinterpret_cast<UDPPacket*>(packet->buffer->data() + ipv4_payload_offset); auto& udp_packet = *reinterpret_cast<UDPPacket*>(packet->buffer->data() + ipv4_payload_offset);
udp_packet.set_source_port(local_port()); udp_packet.set_source_port(local_port());
udp_packet.set_destination_port(peer_port()); udp_packet.set_destination_port(peer_port());
udp_packet.set_length(udp_buffer_size); udp_packet.set_length(udp_buffer_size);
if (!data.read(udp_packet.payload(), data_length)) if (!data.read(udp_packet.payload(), data_length))
return EFAULT; return set_so_error(EFAULT);
routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), routing_decision.next_hop, routing_decision.adapter->fill_in_ipv4_header(*packet, local_address(), routing_decision.next_hop,
peer_address(), IPv4Protocol::UDP, udp_buffer_size, ttl()); peer_address(), IPv4Protocol::UDP, udp_buffer_size, ttl());
@ -126,7 +126,7 @@ KResultOr<u16> UDPSocket::protocol_allocate_local_port()
if (port == first_scan_port) if (port == first_scan_port)
break; break;
} }
return EADDRINUSE; return set_so_error(EADDRINUSE);
}); });
} }
@ -134,7 +134,7 @@ KResult UDPSocket::protocol_bind()
{ {
return sockets_by_port().with_exclusive([&](auto& table) -> KResult { return sockets_by_port().with_exclusive([&](auto& table) -> KResult {
if (table.contains(local_port())) if (table.contains(local_port()))
return EADDRINUSE; return set_so_error(EADDRINUSE);
table.set(local_port(), this); table.set(local_port(), this);
return KSuccess; return KSuccess;
}); });