mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-29 06:02:07 +03:00
LibCore: Use length-checking stream reads and writes for SOCKS5
This commit is contained in:
parent
8032724574
commit
ba354fa396
Notes:
sideshowbarker
2024-07-17 18:46:57 +09:00
Author: https://github.com/timschumi Commit: https://github.com/SerenityOS/serenity/commit/ba354fa396 Pull-request: https://github.com/SerenityOS/serenity/pull/17684 Reviewed-by: https://github.com/JanDeVisser ✅ Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/linusg ✅
@ -39,27 +39,52 @@ struct [[gnu::packed]] Socks5VersionIdentifierAndMethodSelectionMessage {
|
||||
u8 methods[1];
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5VersionIdentifierAndMethodSelectionMessage> : public AK::GenericTraits<Socks5VersionIdentifierAndMethodSelectionMessage> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5InitialResponse {
|
||||
u8 version_identifier;
|
||||
u8 method;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5InitialResponse> : public AK::GenericTraits<Socks5InitialResponse> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5ConnectRequestHeader {
|
||||
u8 version_identifier;
|
||||
u8 command;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5ConnectRequestHeader> : public AK::GenericTraits<Socks5ConnectRequestHeader> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5ConnectRequestTrailer {
|
||||
u16 port;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5ConnectRequestTrailer> : public AK::GenericTraits<Socks5ConnectRequestTrailer> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5ConnectResponseHeader {
|
||||
u8 version_identifier;
|
||||
u8 status;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5ConnectResponseHeader> : public AK::GenericTraits<Socks5ConnectResponseHeader> {
|
||||
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<Socks5UsernamePasswordResponse> : public AK::GenericTraits<Socks5UsernamePasswordResponse> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
namespace {
|
||||
StringView reply_response_name(Reply reply)
|
||||
{
|
||||
@ -102,16 +132,9 @@ ErrorOr<void> 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<Socks5InitialResponse>());
|
||||
|
||||
if (response.version_identifier != to_underlying(version))
|
||||
return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier");
|
||||
@ -135,22 +158,15 @@ ErrorOr<Reply> 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<void> {
|
||||
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<void> {
|
||||
@ -158,62 +174,33 @@ ErrorOr<Reply> send_connect_request_message(Core::Socket& socket, Core::SOCKSPro
|
||||
address_data[0] = to_underlying(AddressType::IPV4);
|
||||
u32 network_ordered_ipv4 = NetworkOrdered<u32>(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<Socks5ConnectResponseHeader>());
|
||||
|
||||
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<u8>());
|
||||
|
||||
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<u8>());
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(response_address_length));
|
||||
TRY(socket.read_until_filled(buffer));
|
||||
break;
|
||||
}
|
||||
case AddressType::IPV6:
|
||||
@ -221,11 +208,7 @@ ErrorOr<Reply> 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<u16>());
|
||||
|
||||
return Reply(response_header.status);
|
||||
}
|
||||
@ -235,46 +218,24 @@ ErrorOr<u8> 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<Socks5UsernamePasswordResponse>());
|
||||
|
||||
if (response.version_identifier != version)
|
||||
return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier");
|
||||
|
Loading…
Reference in New Issue
Block a user