From ba354fa396353fdad62ab4327be4963e3b0b97ee Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Wed, 1 Mar 2023 16:49:00 +0100 Subject: [PATCH] LibCore: Use length-checking stream reads and writes for SOCKS5 --- .../Libraries/LibCore/SOCKSProxyClient.cpp | 143 +++++++----------- 1 file changed, 52 insertions(+), 91 deletions(-) diff --git a/Userland/Libraries/LibCore/SOCKSProxyClient.cpp b/Userland/Libraries/LibCore/SOCKSProxyClient.cpp index 98179980729..88a001c8978 100644 --- a/Userland/Libraries/LibCore/SOCKSProxyClient.cpp +++ b/Userland/Libraries/LibCore/SOCKSProxyClient.cpp @@ -39,27 +39,52 @@ struct [[gnu::packed]] Socks5VersionIdentifierAndMethodSelectionMessage { u8 methods[1]; }; +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + struct [[gnu::packed]] Socks5InitialResponse { u8 version_identifier; u8 method; }; +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + struct [[gnu::packed]] Socks5ConnectRequestHeader { u8 version_identifier; u8 command; u8 reserved; }; +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + struct [[gnu::packed]] Socks5ConnectRequestTrailer { u16 port; }; +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + struct [[gnu::packed]] Socks5ConnectResponseHeader { u8 version_identifier; u8 status; u8 reserved; }; +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + struct [[gnu::packed]] Socks5ConnectResponseTrailer { u8 bind_port; }; @@ -69,6 +94,11 @@ struct [[gnu::packed]] Socks5UsernamePasswordResponse { u8 status; }; +template<> +struct AK::Traits : public AK::GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + namespace { StringView reply_response_name(Reply reply) { @@ -102,16 +132,9 @@ ErrorOr send_version_identifier_and_method_selection_message(Core::Socket& .method_count = 1, .methods = { to_underlying(method) }, }; - // FIXME: This should write the entire span. - auto size = TRY(socket.write_some({ &message, sizeof(message) })); - if (size != sizeof(message)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send version identifier and method selection message"); + TRY(socket.write_value(message)); - Socks5InitialResponse response; - // FIXME: This should read the entire span. - size = TRY(socket.read_some({ &response, sizeof(response) })).size(); - if (size != sizeof(response)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive initial response"); + auto response = TRY(socket.read_value()); if (response.version_identifier != to_underlying(version)) return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier"); @@ -135,22 +158,15 @@ ErrorOr send_connect_request_message(Core::Socket& socket, Core::SOCKSPro .port = htons(port), }; - // FIXME: This should write the entire span. - auto size = TRY(stream.write_some({ &header, sizeof(header) })); - if (size != sizeof(header)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send connect request header"); + TRY(stream.write_value(header)); TRY(target.visit( [&](DeprecatedString const& hostname) -> ErrorOr { u8 address_data[2]; address_data[0] = to_underlying(AddressType::DomainName); address_data[1] = hostname.length(); - // FIXME: This should write the entire span. - auto size = TRY(stream.write_some({ address_data, sizeof(address_data) })); - if (size != array_size(address_data)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send connect request address data"); - // FIXME: This should write the entire span. - TRY(stream.write_some({ hostname.characters(), hostname.length() })); + TRY(stream.write_until_depleted({ address_data, sizeof(address_data) })); + TRY(stream.write_until_depleted({ hostname.characters(), hostname.length() })); return {}; }, [&](u32 ipv4) -> ErrorOr { @@ -158,62 +174,33 @@ ErrorOr send_connect_request_message(Core::Socket& socket, Core::SOCKSPro address_data[0] = to_underlying(AddressType::IPV4); u32 network_ordered_ipv4 = NetworkOrdered(ipv4); memcpy(address_data + 1, &network_ordered_ipv4, sizeof(network_ordered_ipv4)); - // FIXME: This should write the entire span. - auto size = TRY(stream.write_some({ address_data, sizeof(address_data) })); - if (size != array_size(address_data)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send connect request address data"); + TRY(stream.write_until_depleted({ address_data, sizeof(address_data) })); return {}; })); - // FIXME: This should write the entire span. - size = TRY(stream.write_some({ &trailer, sizeof(trailer) })); - if (size != sizeof(trailer)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send connect request trailer"); + TRY(stream.write_value(trailer)); auto buffer = TRY(ByteBuffer::create_uninitialized(stream.used_buffer_size())); TRY(stream.read_until_filled(buffer.bytes())); + TRY(socket.write_until_depleted(buffer)); - // FIXME: This should write the entire span. - size = TRY(socket.write_some({ buffer.data(), buffer.size() })); - if (size != buffer.size()) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send connect request"); - - Socks5ConnectResponseHeader response_header; - // FIXME: This should read the entire span. - size = TRY(socket.read_some({ &response_header, sizeof(response_header) })).size(); - if (size != sizeof(response_header)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive connect response header"); + auto response_header = TRY(socket.read_value()); if (response_header.version_identifier != to_underlying(version)) return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier"); - u8 response_address_type; - // FIXME: This should read the entire span. - size = TRY(socket.read_some({ &response_address_type, sizeof(response_address_type) })).size(); - if (size != sizeof(response_address_type)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive connect response address type"); + auto response_address_type = TRY(socket.read_value()); switch (AddressType(response_address_type)) { case AddressType::IPV4: { u8 response_address_data[4]; - // FIXME: This should read the entire span. - size = TRY(socket.read_some({ response_address_data, sizeof(response_address_data) })).size(); - if (size != sizeof(response_address_data)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive connect response address data"); + TRY(socket.read_until_filled({ response_address_data, sizeof(response_address_data) })); break; } case AddressType::DomainName: { - u8 response_address_length; - // FIXME: This should read the entire span. - size = TRY(socket.read_some({ &response_address_length, sizeof(response_address_length) })).size(); - if (size != sizeof(response_address_length)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive connect response address length"); - ByteBuffer buffer; - buffer.resize(response_address_length); - // FIXME: This should read the entire span. - size = TRY(socket.read_some(buffer)).size(); - if (size != response_address_length) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive connect response address data"); + auto response_address_length = TRY(socket.read_value()); + auto buffer = TRY(ByteBuffer::create_uninitialized(response_address_length)); + TRY(socket.read_until_filled(buffer)); break; } case AddressType::IPV6: @@ -221,11 +208,7 @@ ErrorOr send_connect_request_message(Core::Socket& socket, Core::SOCKSPro return Error::from_string_literal("SOCKS negotiation failed: Invalid connect response address type"); } - u16 bound_port; - // FIXME: This should read the entire span. - size = TRY(socket.read_some({ &bound_port, sizeof(bound_port) })).size(); - if (size != sizeof(bound_port)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive connect response bound port"); + [[maybe_unused]] auto bound_port = TRY(socket.read_value()); return Reply(response_header.status); } @@ -235,46 +218,24 @@ ErrorOr send_username_password_authentication_message(Core::Socket& socket, AllocatingMemoryStream stream; u8 version = 0x01; - // FIXME: This should write the entire span. - auto size = TRY(stream.write_some({ &version, sizeof(version) })); - if (size != sizeof(version)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send username/password authentication message"); + TRY(stream.write_value(version)); u8 username_length = auth_data.username.length(); - // FIXME: This should write the entire span. - size = TRY(stream.write_some({ &username_length, sizeof(username_length) })); - if (size != sizeof(username_length)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send username/password authentication message"); + TRY(stream.write_value(username_length)); - // FIXME: This should write the entire span. - size = TRY(stream.write_some({ auth_data.username.characters(), auth_data.username.length() })); - if (size != auth_data.username.length()) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send username/password authentication message"); + TRY(stream.write_until_depleted({ auth_data.username.characters(), auth_data.username.length() })); u8 password_length = auth_data.password.length(); - // FIXME: This should write the entire span. - size = TRY(stream.write_some({ &password_length, sizeof(password_length) })); - if (size != sizeof(password_length)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send username/password authentication message"); + TRY(stream.write_value(password_length)); - // FIXME: This should write the entire span. - size = TRY(stream.write_some({ auth_data.password.characters(), auth_data.password.length() })); - if (size != auth_data.password.length()) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send username/password authentication message"); + TRY(stream.write_until_depleted({ auth_data.password.characters(), auth_data.password.length() })); auto buffer = TRY(ByteBuffer::create_uninitialized(stream.used_buffer_size())); TRY(stream.read_until_filled(buffer.bytes())); - // FIXME: This should write the entire span. - size = TRY(socket.write_some(buffer)); - if (size != buffer.size()) - return Error::from_string_literal("SOCKS negotiation failed: Failed to send username/password authentication message"); + TRY(socket.write_until_depleted(buffer)); - Socks5UsernamePasswordResponse response; - // FIXME: This should read the entire span. - size = TRY(socket.read_some({ &response, sizeof(response) })).size(); - if (size != sizeof(response)) - return Error::from_string_literal("SOCKS negotiation failed: Failed to receive username/password authentication response"); + auto response = TRY(socket.read_value()); if (response.version_identifier != version) return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier");