1 //--------------------------------------------------------------------------
2 // Copyright (C) 2019-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // http2_stream.cc author Tom Peters <thopeter@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "http2_enum.h"
25 #include "http2_stream.h"
26
27 #include "service_inspectors/http_inspect/http_enum.h"
28 #include "service_inspectors/http_inspect/http_flow_data.h"
29 #include "service_inspectors/http_inspect/http_stream_splitter.h"
30
31 #include "http2_data_cutter.h"
32 #include "http2_dummy_packet.h"
33 #include "http2_flow_data.h"
34
35 using namespace HttpCommon;
36 using namespace Http2Enums;
37 using namespace HttpEnums;
38
Http2Stream(uint32_t stream_id_,Http2FlowData * session_data_)39 Http2Stream::Http2Stream(uint32_t stream_id_, Http2FlowData* session_data_) :
40 stream_id(stream_id_),
41 session_data(session_data_)
42 {
43 }
44
~Http2Stream()45 Http2Stream::~Http2Stream()
46 {
47 delete current_frame;
48 delete hi_flow_data;
49 }
50
eval_frame(const uint8_t * header_buffer,uint32_t header_len,const uint8_t * data_buffer,uint32_t data_len,SourceId source_id)51 void Http2Stream::eval_frame(const uint8_t* header_buffer, uint32_t header_len,
52 const uint8_t* data_buffer, uint32_t data_len, SourceId source_id)
53 {
54 assert(current_frame == nullptr);
55 current_frame = Http2Frame::new_frame(header_buffer, header_len, data_buffer,
56 data_len, session_data, source_id, this);
57 if (!session_data->abort_flow[source_id] && (get_state(source_id) != STREAM_ERROR))
58 {
59 if (current_frame->valid_sequence(state[source_id]))
60 {
61 current_frame->analyze_http1();
62 current_frame->update_stream_state();
63 }
64 else
65 {
66 set_state(source_id, STREAM_ERROR);
67 }
68 }
69 }
70
71 // check if stream is completed, do cleanup if it is
check_and_cleanup_completed()72 void Http2Stream::check_and_cleanup_completed()
73 {
74 if ((state[SRC_CLIENT] >= STREAM_COMPLETE) && (state[SRC_SERVER] >= STREAM_COMPLETE))
75 {
76 if (hi_flow_data != nullptr)
77 {
78 delete hi_flow_data;
79 hi_flow_data = nullptr;
80 }
81 session_data->delete_stream = true;
82 }
83 }
84
clear_frame()85 void Http2Stream::clear_frame()
86 {
87 assert(current_frame != nullptr);
88 current_frame->clear();
89 delete current_frame;
90 current_frame = nullptr;
91
92 check_and_cleanup_completed();
93 }
94
set_state(HttpCommon::SourceId source_id,StreamState new_state)95 void Http2Stream::set_state(HttpCommon::SourceId source_id, StreamState new_state)
96 {
97 assert((STREAM_EXPECT_HEADERS <= new_state) && (new_state <= STREAM_ERROR));
98 assert(state[source_id] < new_state);
99 assert((new_state < STREAM_EXPECT_BODY) || (new_state > STREAM_BODY) ||
100 (get_hi_flow_data() != nullptr));
101 state[source_id] = new_state;
102 }
103
set_hi_flow_data(HttpFlowData * flow_data)104 void Http2Stream::set_hi_flow_data(HttpFlowData* flow_data)
105 {
106 assert(hi_flow_data == nullptr);
107 hi_flow_data = flow_data;
108 }
109
get_buf(unsigned id)110 const Field& Http2Stream::get_buf(unsigned id)
111 {
112 if (current_frame != nullptr)
113 return current_frame->get_buf(id);
114 return Field::FIELD_NULL;
115 }
116
117 #ifdef REG_TEST
print_frame(FILE * output)118 void Http2Stream::print_frame(FILE* output)
119 {
120 if (current_frame != nullptr)
121 current_frame->print_frame(output);
122 }
123 #endif
124
is_open(HttpCommon::SourceId source_id)125 bool Http2Stream::is_open(HttpCommon::SourceId source_id)
126 {
127 return (state[source_id] == STREAM_EXPECT_BODY) || (state[source_id] == STREAM_BODY);
128 }
129
finish_msg_body(HttpCommon::SourceId source_id,bool expect_trailers,bool clear_partial_buffer)130 void Http2Stream::finish_msg_body(HttpCommon::SourceId source_id, bool expect_trailers,
131 bool clear_partial_buffer)
132 {
133 uint32_t http_flush_offset = 0;
134 Http2DummyPacket dummy_pkt;
135 dummy_pkt.flow = session_data->flow;
136 const H2BodyState body_state = expect_trailers ?
137 H2_BODY_COMPLETE_EXPECT_TRAILERS : H2_BODY_COMPLETE;
138 get_hi_flow_data()->finish_h2_body(source_id, body_state, clear_partial_buffer);
139 if (clear_partial_buffer)
140 {
141 uint32_t unused = 0;
142 const snort::StreamSplitter::Status scan_result = session_data->hi_ss[source_id]->scan(
143 &dummy_pkt, nullptr, 0, unused, &http_flush_offset);
144 assert(scan_result == snort::StreamSplitter::FLUSH);
145 UNUSED(scan_result);
146 }
147 }
148