Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
15ab3fd7
Unverified
Commit
15ab3fd7
authored
Jul 23, 2023
by
Samir Halilčević
Committed by
GitHub
Jul 23, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Decode and validate WebSocket closing payload
parent
52668376
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
121 additions
and
1 deletion
+121
-1
libcaf_net/caf/net/web_socket/framing.cpp
libcaf_net/caf/net/web_socket/framing.cpp
+43
-0
libcaf_net/caf/net/web_socket/framing.hpp
libcaf_net/caf/net/web_socket/framing.hpp
+12
-1
libcaf_net/test/net/web_socket/framing.cpp
libcaf_net/test/net/web_socket/framing.cpp
+66
-0
No files found.
libcaf_net/caf/net/web_socket/framing.cpp
View file @
15ab3fd7
...
@@ -11,6 +11,45 @@
...
@@ -11,6 +11,45 @@
namespace
caf
::
net
::
web_socket
{
namespace
caf
::
net
::
web_socket
{
// -- static utility functions -------------------------------------------------
error
framing
::
validate_closing_payload
(
const_byte_span
payload
)
{
if
(
payload
.
empty
())
return
{};
if
(
payload
.
size
()
==
1
)
return
make_error
(
caf
::
sec
::
protocol_error
,
"non empty closing payload must have at least two bytes"
);
auto
status
=
(
std
::
to_integer
<
uint16_t
>
(
payload
[
0
])
<<
8
)
+
std
::
to_integer
<
uint16_t
>
(
payload
[
1
]);
if
(
!
detail
::
rfc3629
::
valid
(
payload
.
subspan
(
2
)))
return
make_error
(
sec
::
protocol_error
,
"malformed UTF-8 text message in closing payload"
);
// statuses between 3000 and 4999 are allowed and application specific
if
(
status
>=
3000
&&
status
<
5000
)
return
{};
// statuses between 1000 and 2999 need to be protocol defined, and status
// codes lower then 1000 and greater or equal then 5000 are invalid.
auto
status_code
=
web_socket
::
status
{
0
};
if
(
from_integer
(
status
,
status_code
))
{
switch
(
status_code
)
{
case
status
:
:
normal_close
:
case
status
:
:
going_away
:
case
status
:
:
protocol_error
:
case
status
:
:
invalid_data
:
case
status
:
:
inconsistent_data
:
case
status
:
:
policy_violation
:
case
status
:
:
message_too_big
:
case
status
:
:
missing_extensions
:
case
status
:
:
unexpected_condition
:
return
{};
default:
break
;
}
}
return
make_error
(
sec
::
protocol_error
,
"invalid status code in closing payload"
);
}
// -- octet_stream::upper_layer implementation ---------------------------------
// -- octet_stream::upper_layer implementation ---------------------------------
error
framing
::
start
(
octet_stream
::
lower_layer
*
down
)
{
error
framing
::
start
(
octet_stream
::
lower_layer
*
down
)
{
...
@@ -212,6 +251,10 @@ ptrdiff_t framing::handle(uint8_t opcode, byte_span payload,
...
@@ -212,6 +251,10 @@ ptrdiff_t framing::handle(uint8_t opcode, byte_span payload,
// opcodes are checked for validity when decoding the header
// opcodes are checked for validity when decoding the header
switch
(
opcode
)
{
switch
(
opcode
)
{
case
detail
:
:
rfc6455
::
connection_close
:
case
detail
:
:
rfc6455
::
connection_close
:
if
(
auto
err
=
validate_closing_payload
(
payload
);
err
)
{
abort_and_shutdown
(
err
);
return
-
1
;
}
abort_and_shutdown
(
sec
::
connection_closed
);
abort_and_shutdown
(
sec
::
connection_closed
);
return
-
1
;
return
-
1
;
case
detail
:
:
rfc6455
::
text_frame
:
{
case
detail
:
:
rfc6455
::
text_frame
:
{
...
...
libcaf_net/caf/net/web_socket/framing.hpp
View file @
15ab3fd7
...
@@ -44,6 +44,14 @@ public:
...
@@ -44,6 +44,14 @@ public:
/// Stored as currently active opcode to mean "no opcode received yet".
/// Stored as currently active opcode to mean "no opcode received yet".
static
constexpr
size_t
nil_code
=
0xFF
;
static
constexpr
size_t
nil_code
=
0xFF
;
// -- static utility functions -----------------------------------------------
/// Checks whether the payload of a closing frame contains a valid status
/// code and an UTF-8 formatted message.
/// @returns A default constructed `error` if the payload is valid, error kind
/// otherwise.
static
error
validate_closing_payload
(
const_byte_span
payload
);
// -- constructors, destructors, and assignment operators --------------------
// -- constructors, destructors, and assignment operators --------------------
/// Creates a new framing protocol for client mode.
/// Creates a new framing protocol for client mode.
...
@@ -148,7 +156,10 @@ private:
...
@@ -148,7 +156,10 @@ private:
// with closing message
// with closing message
template
<
class
...
Ts
>
template
<
class
...
Ts
>
void
abort_and_shutdown
(
sec
reason
,
Ts
&&
...
xs
)
{
void
abort_and_shutdown
(
sec
reason
,
Ts
&&
...
xs
)
{
auto
err
=
make_error
(
reason
,
std
::
forward
<
Ts
>
(
xs
)...);
abort_and_shutdown
(
make_error
(
reason
,
std
::
forward
<
Ts
>
(
xs
)...));
}
void
abort_and_shutdown
(
const
error
&
err
)
{
up_
->
abort
(
err
);
up_
->
abort
(
err
);
shutdown
(
err
);
shutdown
(
err
);
}
}
...
...
libcaf_net/test/net/web_socket/framing.cpp
View file @
15ab3fd7
...
@@ -50,6 +50,15 @@ int fetch_status(const_byte_span payload) {
...
@@ -50,6 +50,15 @@ int fetch_status(const_byte_span payload) {
+
std
::
to_integer
<
int
>
(
payload
[
3
]);
+
std
::
to_integer
<
int
>
(
payload
[
3
]);
}
}
byte_buffer
make_closing_payload
(
uint16_t
code_val
,
std
::
string_view
msg
)
{
byte_buffer
payload
;
payload
.
push_back
(
static_cast
<
std
::
byte
>
((
code_val
&
0xFF00
)
>>
8
));
payload
.
push_back
(
static_cast
<
std
::
byte
>
(
code_val
&
0x00FF
));
auto
*
first
=
reinterpret_cast
<
const
std
::
byte
*>
(
msg
.
data
());
payload
.
insert
(
payload
.
end
(),
first
,
first
+
msg
.
size
());
return
payload
;
}
}
// namespace
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
BEGIN_FIXTURE_SCOPE
(
fixture
)
...
@@ -171,6 +180,25 @@ SCENARIO("the client closes the connection with a closing handshake") {
...
@@ -171,6 +180,25 @@ SCENARIO("the client closes the connection with a closing handshake") {
CHECK_EQ
(
fetch_status
(
transport
->
output_buffer
()),
CHECK_EQ
(
fetch_status
(
transport
->
output_buffer
()),
static_cast
<
int
>
(
net
::
web_socket
::
status
::
normal_close
));
static_cast
<
int
>
(
net
::
web_socket
::
status
::
normal_close
));
}
}
WHEN
(
"the client sends an invalid closing handshake"
)
{
reset
();
std
::
vector
<
std
::
byte
>
handshake
;
// invalid status code
auto
payload
=
make_closing_payload
(
1016
,
""
sv
);
detail
::
rfc6455
::
assemble_frame
(
detail
::
rfc6455
::
connection_close
,
0x0
,
payload
,
handshake
);
transport
->
push
(
handshake
);
}
THEN
(
"the server closes the connection with protocol error"
)
{
CHECK_EQ
(
transport
->
handle_input
(),
0l
);
detail
::
rfc6455
::
header
hdr
;
detail
::
rfc6455
::
decode_header
(
transport
->
output_buffer
(),
hdr
);
CHECK_EQ
(
app
->
abort_reason
,
sec
::
protocol_error
);
CHECK_EQ
(
hdr
.
opcode
,
detail
::
rfc6455
::
connection_close
);
CHECK
(
hdr
.
fin
);
CHECK_EQ
(
fetch_status
(
transport
->
output_buffer
()),
static_cast
<
int
>
(
net
::
web_socket
::
status
::
protocol_error
));
}
}
}
}
}
...
@@ -585,3 +613,41 @@ SCENARIO("the application shuts down on invalid frame fragments") {
...
@@ -585,3 +613,41 @@ SCENARIO("the application shuts down on invalid frame fragments") {
}
}
END_FIXTURE_SCOPE
();
END_FIXTURE_SCOPE
();
TEST_CASE
(
"empty closing payload is valid"
)
{
auto
error
=
net
::
web_socket
::
framing
::
validate_closing_payload
(
byte_buffer
{});
CHECK
(
!
error
);
}
TEST_CASE
(
"decode valid closing codes"
)
{
auto
valid_status_codes
=
std
::
array
{
1000
,
1001
,
1002
,
1003
,
1007
,
1008
,
1009
,
1010
,
1011
,
3000
,
3999
,
4000
,
4999
};
for
(
auto
status_code
:
valid_status_codes
)
{
auto
payload
=
make_closing_payload
(
status_code
,
""
sv
);
auto
error
=
net
::
web_socket
::
framing
::
validate_closing_payload
(
payload
);
CHECK
(
!
error
);
}
}
TEST_CASE
(
"fail on invalid closing codes"
)
{
auto
valid_status_codes
=
std
::
array
{
0
,
999
,
1004
,
1005
,
1006
,
1016
,
1100
,
2000
,
2999
,
5000
,
65535
};
for
(
auto
status_code
:
valid_status_codes
)
{
auto
payload
=
make_closing_payload
(
status_code
,
""
sv
);
auto
result
=
net
::
web_socket
::
framing
::
validate_closing_payload
(
payload
);
CHECK_EQ
(
result
,
sec
::
protocol_error
);
}
}
TEST_CASE
(
"fail on invalid utf8 closing message"
)
{
auto
payload
=
make_closing_payload
(
1000
,
"
\xf4\x80
"
sv
);
auto
result
=
net
::
web_socket
::
framing
::
validate_closing_payload
(
payload
);
CHECK_EQ
(
result
,
sec
::
protocol_error
);
}
TEST_CASE
(
"fail on single byte payload"
)
{
auto
payload
=
byte_buffer
{
std
::
byte
{
0
}};
auto
result
=
net
::
web_socket
::
framing
::
validate_closing_payload
(
payload
);
CHECK_EQ
(
result
,
sec
::
protocol_error
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment