From 0e930707451b12edf719d5389df7857b266a1ba1 Mon Sep 17 00:00:00 2001 From: John Thacker Date: Tue, 13 Sep 2022 18:51:01 -0400 Subject: [PATCH] follow: Add function for sub stream id to registration When dissectors register for Follow Stream, have them register a function for finding the next valid sub stream id for a given stream and substream id pair. This function is NULL if the dissector does not use sub stream IDs. Use this function in follow_stream_dialog to update the sub stream id widget (and use the absence of the function to disable and hide the widget.) Use this function in the CLI tap-follow to determine whether to parse a sub stream id from the command line options. This removes the dependencies on epan/dissectors from the Qt follow_stream_dialog, and gets us closer to having dissectors being able to register for Follow Stream without having to update anything in the common source code. --- epan/dissectors/packet-dccp.c | 2 +- epan/dissectors/packet-http.c | 2 +- epan/dissectors/packet-http2.c | 13 +++- epan/dissectors/packet-quic.c | 13 +++- epan/dissectors/packet-sip.c | 2 +- epan/dissectors/packet-tcp.c | 2 +- epan/dissectors/packet-tls.c | 2 +- epan/dissectors/packet-udp.c | 2 +- epan/follow.c | 9 ++- epan/follow.h | 16 ++++- ui/cli/tap-follow.c | 6 +- ui/qt/follow_stream_dialog.cpp | 110 ++++++++++++++------------------- 12 files changed, 101 insertions(+), 78 deletions(-) diff --git a/epan/dissectors/packet-dccp.c b/epan/dissectors/packet-dccp.c index 2c6282289d..f50d22507b 100644 --- a/epan/dissectors/packet-dccp.c +++ b/epan/dissectors/packet-dccp.c @@ -1990,7 +1990,7 @@ proto_register_dccp(void) register_conversation_table(proto_dccp, FALSE, dccpip_conversation_packet, dccpip_endpoint_packet); register_conversation_filter("dccp", "DCCP", dccp_filter_valid, dccp_build_filter); register_follow_stream(proto_dccp, "dccp_follow", dccp_follow_conv_filter, dccp_follow_index_filter, dccp_follow_address_filter, - dccp_port_to_display, follow_tvb_tap_listener, get_dccp_stream_count); + dccp_port_to_display, follow_tvb_tap_listener, get_dccp_stream_count, NULL); register_init_routine(dccp_init); } diff --git a/epan/dissectors/packet-http.c b/epan/dissectors/packet-http.c index c67fea84b6..ed5ebf87a0 100644 --- a/epan/dissectors/packet-http.c +++ b/epan/dissectors/packet-http.c @@ -4513,7 +4513,7 @@ proto_register_http(void) register_follow_stream(proto_http, "http_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter, tcp_port_to_display, follow_tvb_tap_listener, - get_tcp_stream_count); + get_tcp_stream_count, NULL); http_eo_tap = register_export_object(proto_http, http_eo_packet, NULL); /* compile patterns, exluding "/" */ diff --git a/epan/dissectors/packet-http2.c b/epan/dissectors/packet-http2.c index abfb520f51..73c3b7bb6a 100644 --- a/epan/dissectors/packet-http2.c +++ b/epan/dissectors/packet-http2.c @@ -2489,6 +2489,16 @@ http2_get_stream_id_ge(guint streamid, guint sub_stream_id, guint *sub_stream_id return FALSE; } +gboolean +http2_get_sub_stream_id(guint streamid, guint sub_stream_id, gboolean le, guint *sub_stream_id_out) +{ + if (le) { + return http2_get_stream_id_le(streamid, sub_stream_id, sub_stream_id_out); + } else { + return http2_get_stream_id_ge(streamid, sub_stream_id, sub_stream_id_out); + } +} + static gchar* http2_follow_index_filter(guint stream, guint sub_stream) { @@ -4635,7 +4645,8 @@ proto_register_http2(void) http2_follow_tap = register_tap("http2_follow"); register_follow_stream(proto_http2, "http2_follow", http2_follow_conv_filter, http2_follow_index_filter, tcp_follow_address_filter, - tcp_port_to_display, follow_http2_tap_listener, get_tcp_stream_count); + tcp_port_to_display, follow_http2_tap_listener, get_tcp_stream_count, + http2_get_sub_stream_id); } static void http2_stats_tree_init(stats_tree* st) diff --git a/epan/dissectors/packet-quic.c b/epan/dissectors/packet-quic.c index 8fefd4c9e7..87c9ff52a2 100644 --- a/epan/dissectors/packet-quic.c +++ b/epan/dissectors/packet-quic.c @@ -4551,6 +4551,16 @@ quic_get_stream_id_ge(guint streamid, guint sub_stream_id, guint *sub_stream_id_ return FALSE; } +gboolean +quic_get_sub_stream_id(guint streamid, guint sub_stream_id, gboolean le, guint *sub_stream_id_out) +{ + if (le) { + return quic_get_stream_id_le(streamid, sub_stream_id, sub_stream_id_out); + } else { + return quic_get_stream_id_ge(streamid, sub_stream_id, sub_stream_id_out); + } +} + static gchar * quic_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo, guint *stream, guint *sub_stream) { @@ -5409,7 +5419,8 @@ proto_register_quic(void) register_cleanup_routine(quic_cleanup); register_follow_stream(proto_quic, "quic_follow", quic_follow_conv_filter, quic_follow_index_filter, quic_follow_address_filter, - udp_port_to_display, follow_quic_tap_listener, get_quic_connections_count); + udp_port_to_display, follow_quic_tap_listener, get_quic_connections_count, + quic_get_sub_stream_id); // TODO implement custom reassembly functions that uses the QUIC Connection // ID instead of address and port numbers. diff --git a/epan/dissectors/packet-sip.c b/epan/dissectors/packet-sip.c index cacfec9254..702ebea3c2 100644 --- a/epan/dissectors/packet-sip.c +++ b/epan/dissectors/packet-sip.c @@ -7759,7 +7759,7 @@ void proto_register_sip(void) ws_mempbrk_compile(&pbrk_via_param_end, "\t;, "); register_follow_stream(proto_sip, "sip_follow", sip_follow_conv_filter, sip_follow_index_filter, sip_follow_address_filter, - udp_port_to_display, follow_tvb_tap_listener, NULL); + udp_port_to_display, follow_tvb_tap_listener, NULL, NULL); } void diff --git a/epan/dissectors/packet-tcp.c b/epan/dissectors/packet-tcp.c index 5ace517185..8bb9a7a3ae 100644 --- a/epan/dissectors/packet-tcp.c +++ b/epan/dissectors/packet-tcp.c @@ -9742,7 +9742,7 @@ proto_register_tcp(void) register_conversation_table(proto_mptcp, FALSE, mptcpip_conversation_packet, tcpip_endpoint_packet); register_follow_stream(proto_tcp, "tcp_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter, - tcp_port_to_display, follow_tcp_tap_listener, get_tcp_stream_count); + tcp_port_to_display, follow_tcp_tap_listener, get_tcp_stream_count, NULL); } void diff --git a/epan/dissectors/packet-tls.c b/epan/dissectors/packet-tls.c index eea5e62b53..78bd76ef8b 100644 --- a/epan/dissectors/packet-tls.c +++ b/epan/dissectors/packet-tls.c @@ -4766,7 +4766,7 @@ proto_register_tls(void) "tls", tls_tap); register_follow_stream(proto_tls, "tls", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter, - tcp_port_to_display, ssl_follow_tap_listener, get_tcp_stream_count); + tcp_port_to_display, ssl_follow_tap_listener, get_tcp_stream_count, NULL); secrets_register_type(SECRETS_TYPE_TLS, tls_secrets_block_callback); } diff --git a/epan/dissectors/packet-udp.c b/epan/dissectors/packet-udp.c index 7a07d5fc5b..3b2dd72f04 100644 --- a/epan/dissectors/packet-udp.c +++ b/epan/dissectors/packet-udp.c @@ -1476,7 +1476,7 @@ proto_register_udp(void) register_conversation_table(proto_udp, FALSE, udpip_conversation_packet, udpip_endpoint_packet); register_conversation_filter("udp", "UDP", udp_filter_valid, udp_build_filter); register_follow_stream(proto_udp, "udp_follow", udp_follow_conv_filter, udp_follow_index_filter, udp_follow_address_filter, - udp_port_to_display, follow_tvb_tap_listener, get_udp_stream_count); + udp_port_to_display, follow_tvb_tap_listener, get_udp_stream_count, NULL); register_init_routine(udp_init); } diff --git a/epan/follow.c b/epan/follow.c index 0b1325e01c..11273833d2 100644 --- a/epan/follow.c +++ b/epan/follow.c @@ -29,6 +29,7 @@ struct register_follow { follow_port_to_display_func port_to_display; /* port to name resolution for follow type */ tap_packet_cb tap_handler; /* tap listener handler */ follow_stream_count_func stream_count; /* maximum stream count, used for UI */ + follow_sub_stream_id_func sub_stream_id; /* sub-stream id, used for UI */ }; static wmem_tree_t *registered_followers = NULL; @@ -36,7 +37,7 @@ static wmem_tree_t *registered_followers = NULL; void register_follow_stream(const int proto_id, const char* tap_listener, follow_conv_filter_func conv_filter, follow_index_filter_func index_filter, follow_address_filter_func address_filter, follow_port_to_display_func port_to_display, tap_packet_cb tap_handler, - follow_stream_count_func stream_count) + follow_stream_count_func stream_count, follow_sub_stream_id_func sub_stream_id) { register_follow_t *follower; DISSECTOR_ASSERT(tap_listener); @@ -56,6 +57,7 @@ void register_follow_stream(const int proto_id, const char* tap_listener, follower->port_to_display = port_to_display; follower->tap_handler = tap_handler; follower->stream_count = stream_count; + follower->sub_stream_id = sub_stream_id; if (registered_followers == NULL) registered_followers = wmem_tree_new(wmem_epan_scope()); @@ -109,6 +111,11 @@ follow_stream_count_func get_follow_stream_count_func(register_follow_t* followe return follower->stream_count; } +follow_sub_stream_id_func get_follow_sub_stream_id_func(register_follow_t* follower) +{ + return follower->sub_stream_id; +} + register_follow_t* get_follow_by_name(const char* proto_short_name) { return (register_follow_t*)wmem_tree_lookup_string(registered_followers, proto_short_name, 0); diff --git a/epan/follow.h b/epan/follow.h index 474f1e52c6..f17b3f0c19 100644 --- a/epan/follow.h +++ b/epan/follow.h @@ -111,12 +111,13 @@ typedef gchar* (*follow_index_filter_func)(guint stream, guint sub_stream); typedef gchar* (*follow_address_filter_func)(address* src_addr, address* dst_addr, int src_port, int dst_port); typedef gchar* (*follow_port_to_display_func)(wmem_allocator_t *allocator, guint port); typedef guint32 (*follow_stream_count_func)(void); +typedef gboolean (*follow_sub_stream_id_func)(guint stream, guint sub_stream, gboolean le, guint *sub_stream_out); WS_DLL_PUBLIC void register_follow_stream(const int proto_id, const char* tap_listener, follow_conv_filter_func conv_filter, follow_index_filter_func index_filter, follow_address_filter_func address_filter, follow_port_to_display_func port_to_display, tap_packet_cb tap_handler, - follow_stream_count_func stream_count); + follow_stream_count_func stream_count, follow_sub_stream_id_func sub_stream_id); /** Get protocol ID from registered follower * @@ -182,6 +183,19 @@ WS_DLL_PUBLIC tap_packet_cb get_follow_tap_handler(register_follow_t* follower); */ WS_DLL_PUBLIC follow_stream_count_func get_follow_stream_count_func(register_follow_t* follower); +/** Provide function that, for given stream and sub stream ids, searches for + * the first sub stream id less than or equal (or greater than or equal) the + * given sub stream id present on the given stream id. Returns TRUE and the + * sub stream id found, or FALSE. + * This is used by the GUI to select valid sub stream numbers, e.g. when + * incrementing or decrementing the sub stream ID widget. + * This function should be NULL if the follower does not have sub streams. + * + * @param follower [in] Registered follower + * @return A sub stream id function handler + */ +WS_DLL_PUBLIC follow_sub_stream_id_func get_follow_sub_stream_id_func(register_follow_t* follower); + /** Tap function handler when dissector's tap provides follow data as a tvb. * Used by TCP, UDP and HTTP followers */ diff --git a/ui/cli/tap-follow.c b/ui/cli/tap-follow.c index a56f4b7d4f..ed445e23c4 100644 --- a/ui/cli/tap-follow.c +++ b/ui/cli/tap-follow.c @@ -513,9 +513,9 @@ static void follow_stream(const char *opt_argp, void *userdata) cli_follow_info = g_new0(cli_follow_info_t, 1); cli_follow_info->stream_index = -1; - /* use second parameter only for HTTP2 or QUIC substream */ - if (g_str_equal(proto_filter_name, "http2") || - g_str_equal(proto_filter_name, "quic")) { + /* use second parameter only for followers that have sub streams + * (currently HTTP2 or QUIC) */ + if (get_follow_sub_stream_id_func(follower)) { cli_follow_info->sub_stream_index = -1; } else { cli_follow_info->sub_stream_index = 0; diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp index 4793d7ddd9..b4ce678e5e 100644 --- a/ui/qt/follow_stream_dialog.cpp +++ b/ui/qt/follow_stream_dialog.cpp @@ -15,8 +15,6 @@ #include "frame_tvbuff.h" #include "epan/follow.h" -#include "epan/dissectors/packet-http2.h" -#include "epan/dissectors/packet-quic.h" #include "epan/prefs.h" #include "epan/addr_resolv.h" #include "epan/charsets.h" @@ -256,7 +254,7 @@ void FollowStreamDialog::updateWidgets(bool follow_in_progress) ui->cbDirections->setEnabled(enable); ui->cbCharset->setEnabled(enable); ui->streamNumberSpinBox->setEnabled(enable); - if (follow_type_ == FOLLOW_HTTP2 || follow_type_ == FOLLOW_QUIC) { + if (get_follow_sub_stream_id_func(follower_) != NULL) { ui->subStreamNumberSpinBox->setEnabled(enable); } ui->leFind->setEnabled(enable); @@ -413,24 +411,32 @@ void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num) gboolean ok; if (ui->subStreamNumberSpinBox->isVisible()) { /* We need to find a suitable sub stream for the new stream */ + follow_sub_stream_id_func sub_stream_func; + sub_stream_func = get_follow_sub_stream_id_func(follower_); + + if (sub_stream_func == NULL) { + // Should not happen, this field is only visible for suitable protocols. + return; + } + guint sub_stream_num_new = static_cast(sub_stream_num); if (sub_stream_num < 0) { // Stream ID 0 should always exist as it is used for control messages. + // XXX: That is only guaranteed for HTTP2. For example, in QUIC, + // stream ID 0 is a normal stream used by the first standard client- + // initiated bidirectional stream (if it exists, and it might not) + // and we might have a stream (connection) but only the CRYPTO + // stream, which does not have a (sub) stream ID. + // What should we do if there is a stream with no substream to + // follow? Right now the substream spinbox is left active and + // the user can change the value to no effect. sub_stream_num_new = 0; ok = TRUE; - } else if (follow_type_ == FOLLOW_HTTP2) { - ok = http2_get_stream_id_ge(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - if (!ok) { - ok = http2_get_stream_id_le(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - } - } else if (follow_type_ == FOLLOW_QUIC) { - ok = quic_get_stream_id_ge(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - if (!ok) { - ok = quic_get_stream_id_le(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - } } else { - // Should not happen, this field is only visible for suitable protocols. - return; + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new); + if (!ok) { + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new); + } } sub_stream_num = static_cast(sub_stream_num_new); } else { @@ -457,28 +463,26 @@ void FollowStreamDialog::on_subStreamNumberSpinBox_valueChanged(int sub_stream_n stream_num = ui->streamNumberSpinBox->value(); ui->streamNumberSpinBox->blockSignals(false); + follow_sub_stream_id_func sub_stream_func; + sub_stream_func = get_follow_sub_stream_id_func(follower_); + + if (sub_stream_func == NULL) { + // Should not happen, this field is only visible for suitable protocols. + return; + } + guint sub_stream_num_new = static_cast(sub_stream_num); gboolean ok; /* previous_sub_stream_num_ is a hack to track which buttons was pressed without event handling */ if (sub_stream_num < 0) { // Stream ID 0 should always exist as it is used for control messages. + // XXX: That is only guaranteed for HTTP2, see above. sub_stream_num_new = 0; ok = TRUE; - } else if (follow_type_ == FOLLOW_HTTP2) { - if (previous_sub_stream_num_ < sub_stream_num) { - ok = http2_get_stream_id_ge(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - } else { - ok = http2_get_stream_id_le(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - } - } else if (follow_type_ == FOLLOW_QUIC) { - if (previous_sub_stream_num_ < sub_stream_num) { - ok = quic_get_stream_id_ge(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - } else { - ok = quic_get_stream_id_le(static_cast(stream_num), sub_stream_num_new, &sub_stream_num_new); - } + } else if (previous_sub_stream_num_ < sub_stream_num) { + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new); } else { - // Should not happen, this field is only visible for suitable protocols. - return; + ok = sub_stream_func(static_cast(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new); } sub_stream_num = static_cast(sub_stream_num_new); @@ -1023,15 +1027,6 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, return false; } - /* disable substream spin box for all protocols except HTTP2 and QUIC */ - ui->subStreamNumberSpinBox->blockSignals(true); - ui->subStreamNumberSpinBox->setEnabled(false); - ui->subStreamNumberSpinBox->setValue(0); - ui->subStreamNumberSpinBox->setKeyboardTracking(false); - ui->subStreamNumberSpinBox->blockSignals(false); - ui->subStreamNumberSpinBox->setVisible(false); - ui->subStreamNumberLabel->setVisible(false); - stream_count_func = get_follow_stream_count_func(follower_); if (stream_count_func == NULL) { @@ -1046,45 +1041,30 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, ui->streamNumberLabel->setToolTip(ui->streamNumberSpinBox->toolTip()); } - switch (follow_type_) - { - case FOLLOW_HTTP2: - { + follow_sub_stream_id_func sub_stream_func; + sub_stream_func = get_follow_sub_stream_id_func(follower_); + if (sub_stream_func != NULL) { guint substream_max_id = 0; - http2_get_stream_id_le(static_cast(stream_num), G_MAXINT32, &substream_max_id); + sub_stream_func(static_cast(stream_num), G_MAXINT32, TRUE, &substream_max_id); stream_count = static_cast(substream_max_id); ui->subStreamNumberSpinBox->blockSignals(true); ui->subStreamNumberSpinBox->setEnabled(true); ui->subStreamNumberSpinBox->setMaximum(stream_count); ui->subStreamNumberSpinBox->setValue(sub_stream_num); ui->subStreamNumberSpinBox->blockSignals(false); - ui->subStreamNumberSpinBox->setToolTip(tr("%Ln total sub stream(s).", "", stream_count)); + ui->subStreamNumberSpinBox->setToolTip(tr("Max sub stream ID for the selected stream: %Ln", "", stream_count)); ui->subStreamNumberSpinBox->setToolTip(ui->subStreamNumberSpinBox->toolTip()); ui->subStreamNumberSpinBox->setVisible(true); ui->subStreamNumberLabel->setVisible(true); - - break; - } - case FOLLOW_QUIC: - { - guint substream_max_id = 0; - quic_get_stream_id_le(static_cast(stream_num), G_MAXINT32, &substream_max_id); - stream_count = static_cast(substream_max_id); + } else { + /* disable substream spin box for protocols without substreams */ ui->subStreamNumberSpinBox->blockSignals(true); - ui->subStreamNumberSpinBox->setEnabled(true); - ui->subStreamNumberSpinBox->setMaximum(stream_count); - ui->subStreamNumberSpinBox->setValue(sub_stream_num); + ui->subStreamNumberSpinBox->setEnabled(false); + ui->subStreamNumberSpinBox->setValue(0); + ui->subStreamNumberSpinBox->setKeyboardTracking(false); ui->subStreamNumberSpinBox->blockSignals(false); - ui->subStreamNumberSpinBox->setToolTip(tr("Max QUIC Stream ID for the selected connection: %Ln", "", stream_count)); - ui->subStreamNumberSpinBox->setToolTip(ui->subStreamNumberSpinBox->toolTip()); - ui->subStreamNumberSpinBox->setVisible(true); - ui->subStreamNumberLabel->setVisible(true); - - break; - } - default: - /* No extra handling */ - break; + ui->subStreamNumberSpinBox->setVisible(false); + ui->subStreamNumberLabel->setVisible(false); } beginRetapPackets();