00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
00003 //
00004 // This program is free software; you can redistribute it and/or modify it
00005 // under the terms of the GNU General Public License Version 2 as published
00006 // by the Free Software Foundation. You may not use, modify or distribute
00007 // this program under any other version of the GNU General Public License.
00008 //
00009 // This program is distributed in the hope that it will be useful, but
00010 // WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program; if not, write to the Free Software Foundation, Inc.,
00016 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017 //--------------------------------------------------------------------------
00018 // http_stream_splitter_scan.cc author Tom Peters <thopeter@cisco.com>
00019
00020 #ifdef HAVE_CONFIG_H
00021 #include "config.h"
00022 #endif
00023
00024 #include "http_inspect.h"
00025 #include "http_stream_splitter.h"
00026 #include "http_test_input.h"
00027
00028 using namespace HttpEnums;
00029
00030 // Convenience function. All housekeeping that must be done before we can return FLUSH to stream.
00031 void HttpStreamSplitter::prepare_flush(HttpFlowData* session_data, uint32_t* flush_offset,
00032 SectionType section_type, uint32_t num_flushed, uint32_t num_excess, int32_t num_head_lines,
00033 bool is_broken_chunk, uint32_t num_good_chunks, uint32_t octets_seen, bool strict_length) const
00034 {
00035 session_data->section_type[source_id] = section_type;
00036 session_data->num_excess[source_id] = num_excess;
00037 session_data->num_head_lines[source_id] = num_head_lines;
00038 session_data->is_broken_chunk[source_id] = is_broken_chunk;
00039 session_data->num_good_chunks[source_id] = num_good_chunks;
00040 session_data->octets_expected[source_id] = octets_seen + num_flushed;
00041 session_data->strict_length[source_id] = strict_length;
00042
00043 if (flush_offset != nullptr)
00044 {
00045 #ifdef REG_TEST
00046 if (HttpTestManager::use_test_input())
00047 {
00048 HttpTestManager::get_test_input_source()->flush(num_flushed);
00049 }
00050 else
00051 #endif
00052 *flush_offset = num_flushed;
00053 }
00054 }
00055
00056 HttpCutter* HttpStreamSplitter::get_cutter(SectionType type,
00057 const HttpFlowData* session_data) const
00058 {
00059 switch (type)
00060 {
00061 case SEC_REQUEST:
00062 return (HttpCutter*)new HttpRequestCutter;
00063 case SEC_STATUS:
00064 return (HttpCutter*)new HttpStatusCutter;
00065 case SEC_HEADER:
00066 case SEC_TRAILER:
00067 return (HttpCutter*)new HttpHeaderCutter;
00068 case SEC_BODY_CL:
00069 return (HttpCutter*)new HttpBodyClCutter(session_data->data_length[source_id]);
00070 case SEC_BODY_CHUNK:
00071 return (HttpCutter*)new HttpBodyChunkCutter;
00072 case SEC_BODY_OLD:
00073 return (HttpCutter*)new HttpBodyOldCutter;
00074 default:
00075 assert(false);
00076 return nullptr;
00077 }
00078 }
00079
00080 StreamSplitter::Status HttpStreamSplitter::scan(Flow* flow, const uint8_t* data, uint32_t length,
00081 uint32_t, uint32_t* flush_offset)
00082 {
00083 assert(length <= MAX_OCTETS);
00084
00085 // This is the session state information we share with HttpInspect and store with stream. A
00086 // session is defined by a TCP connection. Since scan() is the first to see a new TCP
00087 // connection the new flow data object is created here.
00088 HttpFlowData* session_data = (HttpFlowData*)flow->get_flow_data(HttpFlowData::inspector_id);
00089
00090 if (session_data == nullptr)
00091 {
00092 flow->set_flow_data(session_data = new HttpFlowData);
00093 HttpModule::increment_peg_counts(PEG_FLOW);
00094 }
00095
00096 SectionType type = session_data->type_expected[source_id];
00097
00098 if (type == SEC_ABORT)
00099 return StreamSplitter::ABORT;
00100
00101 #ifdef REG_TEST
00102 if (HttpTestManager::use_test_input())
00103 {
00104 // This block substitutes a completely new data buffer supplied by the test tool in place
00105 // of the "real" data. It also rewrites the buffer length.
00106 *flush_offset = length;
00107 uint8_t* test_data = nullptr;
00108 HttpTestManager::get_test_input_source()->scan(test_data, length, source_id,
00109 session_data->seq_num);
00110 if (length == 0)
00111 return StreamSplitter::FLUSH;
00112 data = test_data;
00113 }
00114 else if (HttpTestManager::use_test_output())
00115 {
00116 printf("Scan from flow data %" PRIu64
00117 " direction %d length %u client port %hu server port %hu\n", session_data->seq_num,
00118 source_id, length, flow->client_port, flow->server_port);
00119 fflush(stdout);
00120 if (HttpTestManager::get_show_scan())
00121 {
00122 Field(length, data).print(stdout, "Scan segment");
00123 }
00124 }
00125 #endif
00126
00127 assert(!session_data->tcp_close[source_id]);
00128
00129 HttpModule::increment_peg_counts(PEG_SCAN);
00130
00131 // Check for 0.9 response message
00132 if ((type == SEC_STATUS) &&
00133 (session_data->expected_trans_num[SRC_SERVER] == session_data->zero_nine_expected))
00134 {
00135 // 0.9 response is a body that runs to connection end with no headers. HttpInspect does
00136 // not support no headers. Processing this imaginary status line and empty headers allows
00137 // us to overcome this limitation and reuse the entire HTTP infrastructure.
00138 type = SEC_BODY_OLD;
00139 prepare_flush(session_data, nullptr, SEC_STATUS, 14, 0, 0, false, 0, 14, true);
00140 my_inspector->process((const uint8_t*)"HTTP/0.9 200 .", 14, flow, SRC_SERVER, false, NULL);
00141 prepare_flush(session_data, nullptr, SEC_HEADER, 0, 0, 0, false, 0, 0, true);
00142 my_inspector->process((const uint8_t*)"", 0, flow, SRC_SERVER, false, NULL);
00143 }
00144
00145 HttpCutter*& cutter = session_data->cutter[source_id];
00146 if (cutter == nullptr)
00147 {
00148 cutter = get_cutter(type, session_data);
00149 }
00150 const uint32_t max_length = MAX_OCTETS - cutter->get_octets_seen();
00151 const ScanResult cut_result = cutter->cut(data, (length <= max_length) ? length :
00152 max_length, session_data->get_infractions(source_id), session_data->get_events(source_id),
00153 session_data->section_size_target[source_id], session_data->section_size_max[source_id]);
00154 switch (cut_result)
00155 {
00156 case SCAN_NOTFOUND:
00157 if (cutter->get_octets_seen() == MAX_OCTETS)
00158 {
00159 *session_data->get_infractions(source_id) += INF_ENDLESS_HEADER;
00160 // FIXIT-L the following call seems inappropriate for headers and trailers. Those cases
00161 // should be an unconditional EVENT_LOSS_OF_SYNC.
00162 session_data->get_events(source_id)->generate_misformatted_http(data, length);
00163 // FIXIT-H need to process this data not just discard it.
00164 session_data->type_expected[source_id] = SEC_ABORT;
00165 delete cutter;
00166 cutter = nullptr;
00167 return StreamSplitter::ABORT;
00168 }
00169 // Incomplete headers wait patiently for more data
00170 #ifdef REG_TEST
00171 if (HttpTestManager::use_test_input())
00172 return StreamSplitter::FLUSH;
00173 else
00174 #endif
00175 return StreamSplitter::SEARCH;
00176 case SCAN_ABORT:
00177 case SCAN_END: // FIXIT-H need StreamSplitter::END
00178 session_data->type_expected[source_id] = SEC_ABORT;
00179 delete cutter;
00180 cutter = nullptr;
00181 return StreamSplitter::ABORT;
00182 case SCAN_DISCARD:
00183 case SCAN_DISCARD_PIECE:
00184 prepare_flush(session_data, flush_offset, SEC_DISCARD, cutter->get_num_flush(), 0, 0,
00185 false, 0, cutter->get_octets_seen(), true);
00186 if (cut_result == SCAN_DISCARD)
00187 {
00188 delete cutter;
00189 cutter = nullptr;
00190 }
00191 return StreamSplitter::FLUSH;
00192 case SCAN_FOUND:
00193 case SCAN_FOUND_PIECE:
00194 {
00195 const uint32_t flush_octets = cutter->get_num_flush();
00196 prepare_flush(session_data, flush_offset, type, flush_octets, cutter->get_num_excess(),
00197 cutter->get_num_head_lines(), cutter->get_is_broken_chunk(),
00198 cutter->get_num_good_chunks(), cutter->get_octets_seen(),
00199 !((type == SEC_BODY_CL) || (type == SEC_BODY_OLD)));
00200 if (cut_result == SCAN_FOUND)
00201 {
00202 delete cutter;
00203 cutter = nullptr;
00204 }
00205 return StreamSplitter::FLUSH;
00206 }
00207 default:
00208 assert(false);
00209 return StreamSplitter::ABORT;
00210 }
00211 }
00212
END OF CODE