1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-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 // http_transaction.cc author Tom Peters <thopeter@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "http_transaction.h"
25
26 #include "http_common.h"
27 #include "http_enum.h"
28 #include "http_event.h"
29 #include "http_msg_body.h"
30 #include "http_msg_header.h"
31 #include "http_msg_request.h"
32 #include "http_msg_status.h"
33 #include "http_msg_trailer.h"
34
35 using namespace HttpCommon;
36 using namespace HttpEnums;
37 using namespace snort;
38
39 const uint16_t HttpTransaction::transaction_memory_usage_estimate = sizeof(HttpTransaction) +
40 sizeof(HttpMsgRequest) + sizeof(HttpMsgStatus) + (2 * sizeof(HttpMsgHeader)) + sizeof(HttpUri)
41 + (2 * sizeof(HttpInfractions)) + small_things;
42
delete_section_list(HttpMsgSection * section_list)43 static void delete_section_list(HttpMsgSection* section_list)
44 {
45 while (section_list != nullptr)
46 {
47 HttpMsgSection* tmp = section_list;
48 section_list = section_list->next;
49 delete tmp;
50 }
51 }
52
HttpTransaction(HttpFlowData * session_data_)53 HttpTransaction::HttpTransaction(HttpFlowData* session_data_): session_data(session_data_)
54 {
55 infractions[0] = nullptr;
56 infractions[1] = nullptr;
57 }
58
~HttpTransaction()59 HttpTransaction::~HttpTransaction()
60 {
61 delete request;
62 delete status;
63 for (int k = 0; k <= 1; k++)
64 {
65 delete header[k];
66 delete trailer[k];
67 delete infractions[k];
68 }
69 delete_section_list(body_list);
70 delete_section_list(discard_list);
71 }
72
attach_my_transaction(HttpFlowData * session_data,SourceId source_id)73 HttpTransaction* HttpTransaction::attach_my_transaction(HttpFlowData* session_data, SourceId
74 source_id)
75 {
76 // This factory method:
77 // 1. creates new transactions for all request messages and orphaned response messages
78 // 2. associates requests and responses and supports pipelining
79 // 3. garbage collects unneeded transactions
80 // 4. returns the current transaction
81
82 // Request section: replace the old request transaction with a new transaction.
83 if (session_data->section_type[source_id] == SEC_REQUEST)
84 {
85 // If the HTTP request and response messages are alternating (usual situation) the old
86 // request transaction will have been moved to the server side when the last response
87 // message was received. This will be nullptr and we don't need to deal with the old
88 // request transaction here.
89 if (session_data->transaction[SRC_CLIENT] != nullptr)
90 {
91 // The old request transaction is still here. Typically that is because the
92 // the current request has arrived before the previous response (pipelining). We need
93 // to add this transaction to our pipeline where it will wait for the matching
94 // response. But there are some special cases to check first.
95 if (session_data->transaction[SRC_CLIENT]->response_seen)
96 {
97 // The response started before the request finished. When the response took the
98 // old request transaction it did not leave the usual nullptr because we still
99 // needed it. Instead the two sides have been sharing the transaction. This is a
100 // soft delete that eliminates our interest in this transaction without disturbing
101 // the possibly ongoing response processing.
102 delete_transaction(session_data->transaction[SRC_CLIENT], session_data);
103 }
104 else if ((session_data->pipeline_overflow) || (session_data->pipeline_underflow))
105 {
106 // Pipelining previously broke down and both sides are processed separately from
107 // now on. We just throw things away when we are done with them.
108 delete_transaction(session_data->transaction[SRC_CLIENT], session_data);
109 }
110 else if (!session_data->add_to_pipeline(session_data->transaction[SRC_CLIENT]))
111 {
112 // The pipeline is full and just overflowed.
113 *session_data->infractions[source_id] += INF_PIPELINE_OVERFLOW;
114 session_data->events[source_id]->create_event(EVENT_PIPELINE_MAX);
115 delete_transaction(session_data->transaction[SRC_CLIENT], session_data);
116 }
117 }
118 session_data->transaction[SRC_CLIENT] = new HttpTransaction(session_data);
119
120 // The StreamSplitter generates infractions related to this transaction while splitting the
121 // request line and keeps them in temporary storage in the FlowData. Now we move them here.
122 session_data->transaction[SRC_CLIENT]->infractions[SRC_CLIENT] =
123 session_data->infractions[SRC_CLIENT];
124 session_data->infractions[SRC_CLIENT] = nullptr;
125 }
126 // This transaction has more than one response. This is a new response which is replacing the
127 // interim response. The two responses cannot coexist so we must clean up the interim response.
128 else if ((session_data->section_type[source_id] == SEC_STATUS) &&
129 (session_data->transaction[SRC_SERVER] != nullptr) &&
130 session_data->transaction[SRC_SERVER]->second_response_expected)
131 {
132 session_data->transaction[SRC_SERVER]->second_response_expected = false;
133 session_data->transaction[SRC_SERVER]->discard_section(
134 session_data->transaction[SRC_SERVER]->status);
135 session_data->transaction[SRC_SERVER]->status = nullptr;
136 session_data->transaction[SRC_SERVER]->discard_section(
137 session_data->transaction[SRC_SERVER]->header[SRC_SERVER]);
138 session_data->transaction[SRC_SERVER]->header[SRC_SERVER] = nullptr;
139 }
140 // Status section: delete the current transaction and get a new one from the pipeline. If the
141 // pipeline is empty check for a request transaction and take it. If there is no transaction
142 // available then declare an underflow and create a new transaction specifically for the
143 // response side.
144 else if (session_data->section_type[source_id] == SEC_STATUS)
145 {
146 delete_transaction(session_data->transaction[SRC_SERVER], session_data);
147 if (session_data->pipeline_underflow)
148 {
149 // A previous underflow separated the two sides forever
150 session_data->transaction[SRC_SERVER] = new HttpTransaction(session_data);
151 }
152 else if ((session_data->transaction[SRC_SERVER] = session_data->take_from_pipeline()) ==
153 nullptr)
154 {
155 if ((session_data->transaction[SRC_CLIENT] == nullptr) ||
156 (session_data->transaction[SRC_CLIENT]->response_seen))
157 {
158 // Either there is no request at all or there is a request but a previous response
159 // already took it. Either way we have more responses than requests.
160 session_data->pipeline_underflow = true;
161 session_data->transaction[SRC_SERVER] = new HttpTransaction(session_data);
162 }
163
164 else if (session_data->type_expected[SRC_CLIENT] == SEC_REQUEST)
165 {
166 // This is the normal case where the requests and responses are alternating (no
167 // pipelining). Processing of the response is complete so the request just takes
168 // it.
169 session_data->transaction[SRC_SERVER] = session_data->transaction[SRC_CLIENT];
170 session_data->transaction[SRC_CLIENT] = nullptr;
171 }
172 else
173 {
174 // Response message is starting before the request message has finished. Request
175 // side is not finished with this transaction so two sides share it
176 session_data->transaction[SRC_CLIENT]->shared_ownership = true;
177 session_data->transaction[SRC_SERVER] = session_data->transaction[SRC_CLIENT];
178 }
179 }
180 session_data->transaction[SRC_SERVER]->response_seen = true;
181
182 // Move in server infractions now that the response is attached here
183 session_data->transaction[SRC_SERVER]->infractions[SRC_SERVER] =
184 session_data->infractions[SRC_SERVER];
185 session_data->infractions[SRC_SERVER] = nullptr;
186 }
187
188 assert(session_data->transaction[source_id] != nullptr);
189 session_data->transaction[source_id]->active_sections++;
190 return session_data->transaction[source_id];
191 }
192
discard_section(HttpMsgSection * section)193 void HttpTransaction::discard_section(HttpMsgSection* section)
194 {
195 if (section != nullptr)
196 {
197 section->next = discard_list;
198 discard_list = section;
199 }
200 }
201
clear_section()202 void HttpTransaction::clear_section()
203 {
204 assert(active_sections > 0);
205 active_sections--;
206 }
207
garbage_collect()208 void HttpTransaction::garbage_collect()
209 {
210 HttpMsgSection** current = (HttpMsgSection**)&body_list;
211 while (*current != nullptr)
212 {
213 if ((*current)->is_clear())
214 {
215 HttpMsgSection* tmp = *current;
216 *current = (*current)->next;
217 delete tmp;
218 }
219 else
220 current = &(*current)->next;
221 }
222 }
223
delete_transaction(HttpTransaction * transaction,HttpFlowData * session_data)224 void HttpTransaction::delete_transaction(HttpTransaction* transaction, HttpFlowData* session_data)
225 {
226 if (transaction != nullptr)
227 {
228 if (!transaction->shared_ownership)
229 {
230 if ((transaction->active_sections > 0) && (session_data != nullptr))
231 {
232 transaction->next = session_data->discard_list;
233 session_data->discard_list = transaction;
234 }
235 else
236 delete transaction;
237 }
238 else
239 transaction->shared_ownership = false;
240 }
241 }
242
set_body(HttpMsgBody * latest_body)243 void HttpTransaction::set_body(HttpMsgBody* latest_body)
244 {
245 latest_body->next = body_list;
246 body_list = latest_body;
247 }
248
get_infractions(SourceId source_id)249 HttpInfractions* HttpTransaction::get_infractions(SourceId source_id)
250 {
251 return infractions[source_id];
252 }
253
set_one_hundred_response()254 void HttpTransaction::set_one_hundred_response()
255 {
256 assert(response_seen);
257 if (one_hundred_response)
258 {
259 *infractions[SRC_SERVER] += INF_MULTIPLE_100_RESPONSES;
260 session_data->events[SRC_SERVER]->create_event(EVENT_MULTIPLE_100_RESPONSES);
261 }
262 one_hundred_response = true;
263 second_response_expected = true;
264 }
265