Skip to content

Commit f48ebb6

Browse files
committed
Rename tls_events fields for future compatibility with non handshake TLS records. Update JSON parsing to better match new structure
Signed-off-by: Dom Del Nano <ddelnano@gmail.com>
1 parent 37bcf97 commit f48ebb6

File tree

5 files changed

+62
-31
lines changed

5 files changed

+62
-31
lines changed

src/stirling/source_connectors/socket_tracer/protocols/tls/parse.cc

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ namespace stirling {
3131
namespace protocols {
3232
namespace tls {
3333

34+
using px::utils::JSONObjectBuilder;
35+
3436
constexpr size_t kTLSRecordHeaderLength = 5;
3537
constexpr size_t kExtensionMinimumLength = 4;
3638
constexpr size_t kSNIExtensionMinimumLength = 3;
@@ -39,11 +41,9 @@ constexpr size_t kSNIExtensionMinimumLength = 3;
3941
// In TLS 1.2 and earlier, gmt_unix_time is 4 bytes and Random is 28 bytes.
4042
constexpr size_t kRandomStructLength = 32;
4143

42-
StatusOr<ParseState> ExtractSNIExtension(std::map<std::string, std::string>* exts,
43-
BinaryDecoder* decoder) {
44+
StatusOr<ParseState> ExtractSNIExtension(ReqExtensions* exts, BinaryDecoder* decoder) {
4445
PX_ASSIGN_OR(auto server_name_list_length, decoder->ExtractBEInt<uint16_t>(),
4546
return ParseState::kInvalid);
46-
std::vector<std::string> server_names;
4747
while (server_name_list_length > 0) {
4848
PX_ASSIGN_OR(auto server_name_type, decoder->ExtractBEInt<uint8_t>(),
4949
return error::Internal("Failed to extract server name type"));
@@ -56,10 +56,9 @@ StatusOr<ParseState> ExtractSNIExtension(std::map<std::string, std::string>* ext
5656
PX_ASSIGN_OR(auto server_name, decoder->ExtractString(server_name_length),
5757
return error::Internal("Failed to extract server name"));
5858

59-
server_names.push_back(std::string(server_name));
59+
exts->server_names.push_back(std::string(server_name));
6060
server_name_list_length -= kSNIExtensionMinimumLength + server_name_length;
6161
}
62-
exts->insert({"server_name", ToJSONString(server_names)});
6362
return ParseState::kSuccess;
6463
}
6564

@@ -162,6 +161,8 @@ ParseState ParseFullFrame(BinaryDecoder* decoder, Frame* frame) {
162161
return ParseState::kSuccess;
163162
}
164163

164+
ReqExtensions req_extensions;
165+
RespExtensions resp_extensions;
165166
while (extensions_length > 0) {
166167
PX_ASSIGN_OR(auto extension_type, decoder->ExtractBEInt<uint16_t>(),
167168
return ParseState::kInvalid);
@@ -170,7 +171,7 @@ ParseState ParseFullFrame(BinaryDecoder* decoder, Frame* frame) {
170171

171172
if (extension_length > 0) {
172173
if (extension_type == 0x00) {
173-
if (!ExtractSNIExtension(&frame->extensions, decoder).ok()) {
174+
if (!ExtractSNIExtension(&req_extensions, decoder).ok()) {
174175
return ParseState::kInvalid;
175176
}
176177
} else {
@@ -182,6 +183,13 @@ ParseState ParseFullFrame(BinaryDecoder* decoder, Frame* frame) {
182183

183184
extensions_length -= kExtensionMinimumLength + extension_length;
184185
}
186+
JSONObjectBuilder req_body_builder;
187+
req_body_builder.WriteKVRecursive("extensions", req_extensions);
188+
frame->req_body = req_body_builder.GetString();
189+
190+
JSONObjectBuilder resp_body_builder;
191+
resp_body_builder.WriteKVRecursive("extensions", resp_extensions);
192+
frame->resp_body = resp_body_builder.GetString();
185193

186194
return ParseState::kSuccess;
187195
}

src/stirling/source_connectors/socket_tracer/protocols/tls/types.h

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ namespace stirling {
4343
namespace protocols {
4444
namespace tls {
4545

46-
using ::px::utils::ToJSONString;
47-
4846
enum class ContentType : uint8_t {
4947
kChangeCipherSpec = 0x14,
5048
kAlert = 0x15,
@@ -186,6 +184,25 @@ enum class ExtensionType : uint16_t {
186184
kRenegotiationInfo = 65281,
187185
};
188186

187+
// Extensions that are common to both the client and server side
188+
// of a TLS handshake
189+
struct SharedExtensions {
190+
void ToJSON(::px::utils::JSONObjectBuilder* /*builder*/) const {}
191+
};
192+
193+
struct ReqExtensions : public SharedExtensions {
194+
std::vector<std::string> server_names;
195+
196+
void ToJSON(::px::utils::JSONObjectBuilder* builder) const {
197+
SharedExtensions::ToJSON(builder);
198+
builder->WriteKV("server_name", server_names);
199+
}
200+
};
201+
202+
struct RespExtensions : public SharedExtensions {
203+
void ToJSON(::px::utils::JSONObjectBuilder* builder) const { SharedExtensions::ToJSON(builder); }
204+
};
205+
189206
struct Frame : public FrameBase {
190207
ContentType content_type;
191208

@@ -200,7 +217,8 @@ struct Frame : public FrameBase {
200217
LegacyVersion handshake_version;
201218

202219
std::string session_id;
203-
std::map<std::string, std::string> extensions;
220+
std::string req_body;
221+
std::string resp_body;
204222

205223
bool consumed = false;
206224

@@ -209,9 +227,9 @@ struct Frame : public FrameBase {
209227
std::string ToString() const override {
210228
return absl::Substitute(
211229
"TLS Frame [len=$0 content_type=$1 legacy_version=$2 handshake_version=$3 "
212-
"handshake_type=$4 extensions=$5]",
213-
length, content_type, legacy_version, handshake_version, handshake_type,
214-
ToJSONString(extensions));
230+
"handshake_type=$4 req_body=$5 resp_body=$6]",
231+
length, content_type, legacy_version, handshake_version, handshake_type, req_body,
232+
resp_body);
215233
}
216234
};
217235

src/stirling/source_connectors/socket_tracer/socket_trace_connector.cc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,12 @@ using px::utils::ToJSONString;
203203
// Most HTTP servers support 8K headers, so we truncate after that.
204204
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
205205
constexpr size_t kMaxHTTPHeadersBytes = 8192;
206-
// TLS records have a maximum size of 16KiB. While there isn't a size limit
207-
// for the extensions, we limit it to 1 KiB to avoid excessive memory usage.
208-
// A typical ClientHello from curl is around 500 bytes. This assumes that
206+
// TLS records have a maximum size of 16KiB. The bulk of the body columns are extensions
207+
// and while there isn't a size limit for them, we limit it to 1 KiB to avoid excessive
208+
// memory usage. A typical ClientHello from curl is around 500 bytes. This assumes that
209209
// all extensions are captured, but we won't support capturing all extensions and
210210
// will avoid large extensions like the padding extension,
211-
constexpr size_t kMaxTLSExtensionsBytes = 1024;
211+
constexpr size_t kMaxTLSBodyBytes = 1024;
212212

213213
// Protobuf printer will limit strings to this length.
214214
constexpr size_t kMaxPBStringLen = 64;
@@ -1721,9 +1721,10 @@ void SocketTraceConnector::AppendMessage(ConnectorContext* ctx, const ConnTracke
17211721
r.Append<r.ColIndex("local_addr")>(conn_tracker.local_endpoint().AddrStr());
17221722
r.Append<r.ColIndex("local_port")>(conn_tracker.local_endpoint().port());
17231723
r.Append<r.ColIndex("trace_role")>(conn_tracker.role());
1724-
r.Append<r.ColIndex("req_type")>(static_cast<uint64_t>(req_message.content_type));
1724+
r.Append<r.ColIndex("content_type")>(static_cast<uint64_t>(req_message.content_type));
17251725
r.Append<r.ColIndex("version")>(static_cast<uint64_t>(req_message.legacy_version));
1726-
r.Append<r.ColIndex("extensions")>(ToJSONString(req_message.extensions), kMaxTLSExtensionsBytes);
1726+
r.Append<r.ColIndex("req_body")>(req_message.req_body, kMaxTLSBodyBytes);
1727+
r.Append<r.ColIndex("resp_body")>(resp_message.resp_body, kMaxTLSBodyBytes);
17271728
r.Append<r.ColIndex("latency")>(
17281729
CalculateLatency(req_message.timestamp_ns, resp_message.timestamp_ns));
17291730
#ifndef NDEBUG

src/stirling/source_connectors/socket_tracer/tls_table.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,22 @@ static constexpr DataElement kTLSElements[] = {
3737
canonical_data_elements::kLocalAddr,
3838
canonical_data_elements::kLocalPort,
3939
canonical_data_elements::kTraceRole,
40-
{"req_type", "The type of request from the TLS record (Client/ServerHello, etc.)",
40+
{"content_type", "The content type of the TLS record (e.g. handshake, alert, heartbeat, etc)",
4141
types::DataType::INT64,
4242
types::SemanticType::ST_NONE,
4343
types::PatternType::GENERAL_ENUM},
4444
{"version", "Version of TLS record",
4545
types::DataType::INT64,
4646
types::SemanticType::ST_NONE,
4747
types::PatternType::GENERAL_ENUM},
48-
{"extensions", "Extensions in the TLS record",
48+
{"req_body", "Request body in JSON format. Structure depends on content type (e.g. handshakes contain TLS extensions)",
4949
types::DataType::STRING,
5050
types::SemanticType::ST_NONE,
51-
types::PatternType::GENERAL},
51+
types::PatternType::STRUCTURED},
52+
{"resp_body", "Response body in JSON format. Structure depends on content type (e.g. handshakes contain TLS extensions)",
53+
types::DataType::STRING,
54+
types::SemanticType::ST_NONE,
55+
types::PatternType::STRUCTURED},
5256
canonical_data_elements::kLatencyNS,
5357
#ifndef NDEBUG
5458
canonical_data_elements::kPXInfo,
@@ -61,9 +65,9 @@ static constexpr auto kTLSTable =
6165
DEFINE_PRINT_TABLE(TLS)
6266

6367
constexpr int kTLSUPIDIdx = kTLSTable.ColIndex("upid");
64-
constexpr int kTLSCmdIdx = kTLSTable.ColIndex("req_type");
68+
constexpr int kTLSCmdIdx = kTLSTable.ColIndex("content_type");
6569
constexpr int kTLSVersionIdx = kTLSTable.ColIndex("version");
66-
constexpr int kTLSExtensionsIdx = kTLSTable.ColIndex("extensions");
70+
constexpr int kTLSReqBodyIdx = kTLSTable.ColIndex("req_body");
6771

6872
} // namespace stirling
6973
} // namespace px

src/stirling/source_connectors/socket_tracer/tls_trace_bpf_test.cc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ using ::testing::UnorderedElementsAre;
5050

5151
struct TraceRecords {
5252
std::vector<tls::Record> tls_records;
53-
std::vector<std::string> tls_extensions;
53+
std::vector<std::string> req_body;
5454
};
5555

5656
class NginxOpenSSL_3_0_8_ContainerWrapper
@@ -80,11 +80,11 @@ tls::Record GetExpectedTLSRecord() {
8080
return expected_record;
8181
}
8282

83-
inline std::vector<std::string> GetExtensions(const types::ColumnWrapperRecordBatch& rb,
84-
const std::vector<size_t>& indices) {
83+
inline std::vector<std::string> GetRequestBody(const types::ColumnWrapperRecordBatch& rb,
84+
const std::vector<size_t>& indices) {
8585
std::vector<std::string> exts;
8686
for (size_t idx : indices) {
87-
exts.push_back(rb[kTLSExtensionsIdx]->Get<types::StringValue>(idx));
87+
exts.push_back(rb[kTLSReqBodyIdx]->Get<types::StringValue>(idx));
8888
}
8989
return exts;
9090
}
@@ -127,9 +127,9 @@ class TLSVersionParameterizedTest
127127

128128
TraceRecords records = this->GetTraceRecords(this->server_.PID());
129129
EXPECT_THAT(records.tls_records, SizeIs(1));
130-
EXPECT_THAT(records.tls_extensions, SizeIs(1));
131-
auto sni_str = R"({"server_name":"[\"test-host\"]"})";
132-
EXPECT_THAT(records.tls_extensions[0], StrEq(sni_str));
130+
EXPECT_THAT(records.req_body, SizeIs(1));
131+
auto sni_str = R"({"extensions":{"server_name":["test-host"]}})";
132+
EXPECT_THAT(records.req_body[0], StrEq(sni_str));
133133
}
134134

135135
// Returns the trace records of the process specified by the input pid.
@@ -144,7 +144,7 @@ class TLSVersionParameterizedTest
144144
FindRecordIdxMatchesPID(record_batch, kTLSUPIDIdx, pid);
145145
std::vector<tls::Record> tls_records =
146146
ToRecordVector<tls::Record>(record_batch, server_record_indices);
147-
std::vector<std::string> extensions = GetExtensions(record_batch, server_record_indices);
147+
std::vector<std::string> extensions = GetRequestBody(record_batch, server_record_indices);
148148

149149
return {std::move(tls_records), std::move(extensions)};
150150
}

0 commit comments

Comments
 (0)