Branch data Line data Source code
1 : : // $Id: ContentLine.cc,v 1.1.2.8 2006/06/01 01:55:42 sommer Exp $
2 : :
3 : : #include "ContentLine.h"
4 : : #include "TCP.h"
5 : :
6 : 1386 : ContentLine_Analyzer::ContentLine_Analyzer(Connection* conn, bool orig)
7 : 1386 : : TCP_SupportAnalyzer(AnalyzerTag::ContentLine, conn, orig)
8 : : {
9 : 1386 : InitState();
10 : 1386 : }
11 : :
12 : : ContentLine_Analyzer::ContentLine_Analyzer(AnalyzerTag::Tag tag,
13 : 0 : Connection* conn, bool orig)
14 : 0 : : TCP_SupportAnalyzer(tag, conn, orig)
15 : : {
16 : 0 : InitState();
17 : 0 : }
18 : :
19 : 1386 : void ContentLine_Analyzer::InitState()
20 : : {
21 : 1386 : flag_NULs = 0;
22 : 1386 : CR_LF_as_EOL = (CR_as_EOL | LF_as_EOL);
23 : 1386 : skip_deliveries = 0;
24 : 1386 : skip_partial = 0;
25 : 1386 : buf = 0;
26 : 1386 : seq_delivered_in_lines = 0;
27 : 1386 : skip_pending = 0;
28 : 1386 : seq = 0;
29 : 1386 : seq_to_skip = 0;
30 : 1386 : plain_delivery_length = 0;
31 : 1386 : is_plain = 0;
32 : :
33 : 1386 : InitBuffer(0);
34 : 1386 : }
35 : :
36 : 1386 : void ContentLine_Analyzer::InitBuffer(int size)
37 : : {
38 [ - + ][ # # ]: 1386 : if ( buf && buf_len >= size )
39 : : // Don't shrink the buffer, because it's not clear in that
40 : : // case how to deal with characters in it that no longer fit.
41 : 0 : return;
42 : :
43 [ + - ]: 1386 : if ( size < 128 )
44 : 1386 : size = 128;
45 : :
46 : 1386 : u_char* b = new u_char[size];
47 : :
48 [ - + ]: 1386 : if ( buf )
49 : : {
50 [ # # ]: 0 : if ( offset > 0 )
51 : 0 : memcpy(b, buf, offset);
52 [ # # ]: 0 : delete [] buf;
53 : : }
54 : : else
55 : : {
56 : 1386 : offset = 0;
57 : 1386 : last_char = 0;
58 : : }
59 : :
60 : 1386 : buf = b;
61 : 1386 : buf_len = size;
62 : : }
63 : :
64 : 1386 : ContentLine_Analyzer::~ContentLine_Analyzer()
65 : : {
66 [ + - ][ # # ]: 1386 : delete [] buf;
[ # # ]
67 [ + - ][ # # ]: 1386 : }
[ # # ]
68 : :
69 : 0 : int ContentLine_Analyzer::HasPartialLine() const
70 : : {
71 [ # # ][ # # ]: 0 : return buf && offset > 0;
72 : : }
73 : :
74 : : void ContentLine_Analyzer::DeliverStream(int len, const u_char* data,
75 : 5789 : bool is_orig)
76 : : {
77 : 5789 : TCP_SupportAnalyzer::DeliverStream(len, data, is_orig);
78 : :
79 [ + - + + ]: 5789 : if ( len <= 0 || SkipDeliveries() )
[ + + ]
80 : 1580 : return;
81 : :
82 [ + + ]: 4209 : if ( skip_partial )
83 : : {
84 : : TCP_Analyzer* tcp =
85 : 3380 : static_cast<TCP_ApplicationAnalyzer*>(Parent())->TCP();
86 : :
87 [ + - + + ]: 3380 : if ( tcp && tcp->IsPartial() )
[ + + ]
88 : 1191 : return;
89 : : }
90 : :
91 [ + - ][ + + ]: 3018 : if ( buf && len + offset >= buf_len )
92 : : { // Make sure we have enough room to accommodate the new stuff.
93 : 743 : int old_buf_len = buf_len;
94 : 743 : buf_len = ((offset + len) * 3) / 2 + 1;
95 : :
96 : 743 : u_char* tmp = new u_char[buf_len];
97 [ + + ]: 169949 : for ( int i = 0; i < old_buf_len; ++i )
98 : 169206 : tmp[i] = buf[i];
99 : :
100 [ + - ]: 743 : delete [] buf;
101 : 743 : buf = tmp;
102 : :
103 [ - + ]: 743 : if ( ! buf )
104 : 0 : internal_error("out of memory delivering endpoint line");
105 : : }
106 : :
107 : 3018 : DoDeliver(len, data);
108 : :
109 : 5789 : seq += len;
110 : : }
111 : :
112 : 338 : void ContentLine_Analyzer::Undelivered(int seq, int len, bool orig)
113 : : {
114 : 338 : ForwardUndelivered(seq, len, orig);
115 : 338 : }
116 : :
117 : 717 : void ContentLine_Analyzer::EndpointEOF(bool is_orig)
118 : : {
119 [ + + ]: 717 : if ( offset > 0 )
120 : 8 : DeliverStream(1, (const u_char*) "\n", is_orig);
121 : 717 : }
122 : :
123 : 927 : void ContentLine_Analyzer::SetPlainDelivery(int length)
124 : : {
125 [ - + ]: 927 : if ( length < 0 )
126 : 0 : internal_error("negative length for plain delivery");
127 : :
128 : 927 : plain_delivery_length = length;
129 : 927 : }
130 : :
131 : 3018 : void ContentLine_Analyzer::DoDeliver(int len, const u_char* data)
132 : : {
133 : 3018 : seq_delivered_in_lines = seq;
134 : :
135 [ + + ][ + - ]: 17558 : while ( len > 0 && ! SkipDeliveries() )
[ + + ]
136 : : {
137 [ + - ][ + + ]: 14540 : if ( (CR_LF_as_EOL & CR_as_EOL) &&
[ + - ]
138 : : last_char == '\r' && *data == '\n' )
139 : : {
140 : : // CR is already considered as EOL.
141 : : // Compress CRLF to just one line termination.
142 : : //
143 : : // Note, we test this prior to checking for
144 : : // "plain delivery" because (1) we might have
145 : : // made the decision to switch to plain delivery
146 : : // based on a line terminated with '\r' for
147 : : // which a '\n' then arrived, and (2) we are
148 : : // careful when executing plain delivery to
149 : : // clear last_char once we do so.
150 : 2 : last_char = *data;
151 : 2 : --len; ++data; ++seq;
152 : 2 : ++seq_delivered_in_lines;
153 : : }
154 : :
155 [ + + ]: 14540 : if ( plain_delivery_length > 0 )
156 : : {
157 : 2121 : int deliver_plain = min(plain_delivery_length, len);
158 : :
159 : 2121 : last_char = 0; // clear last_char
160 : 2121 : plain_delivery_length -= deliver_plain;
161 : 2121 : is_plain = 1;
162 : :
163 : 2121 : ForwardStream(deliver_plain, data, IsOrig());
164 : :
165 : 2121 : is_plain = 0;
166 : :
167 : 2121 : data += deliver_plain;
168 : 2121 : len -= deliver_plain;
169 [ + + ]: 2121 : if ( len == 0 )
170 : 2023 : return;
171 : : }
172 : :
173 [ - + ]: 12517 : if ( skip_pending > 0 )
174 : 0 : SkipBytes(skip_pending);
175 : :
176 : : // Note that the skipping must take place *after*
177 : : // the CR/LF check above, so that the '\n' of the
178 : : // previous line is skipped first.
179 [ - + ]: 12517 : if ( seq < seq_to_skip )
180 : : {
181 : : // Skip rest of the data and return
182 : 0 : int skip_len = seq_to_skip - seq;
183 [ # # ]: 0 : if ( skip_len > len )
184 : 0 : skip_len = len;
185 : :
186 : 0 : ForwardUndelivered(seq, skip_len, IsOrig());
187 : :
188 : 0 : len -= skip_len; data += skip_len; seq += skip_len;
189 : 0 : seq_delivered_in_lines += skip_len;
190 : : }
191 : :
192 [ + + ]: 12517 : if ( len <= 0 )
193 : 1 : break;
194 : :
195 : 12516 : int n = DoDeliverOnce(len, data);
196 : 12516 : len -= n;
197 : 12516 : data += n;
198 : 12516 : seq += n;
199 : : }
200 : : }
201 : :
202 : 12516 : int ContentLine_Analyzer::DoDeliverOnce(int len, const u_char* data)
203 : : {
204 : 12516 : const u_char* data_start = data;
205 : :
206 [ - + ]: 12516 : if ( len <= 0 )
207 : 0 : return 0;
208 : :
209 [ + + ]: 694064 : for ( ; len > 0; --len, ++data )
210 : : {
211 [ - + ]: 693881 : if ( offset >= buf_len )
212 : 0 : InitBuffer(buf_len * 2);
213 : :
214 : 693881 : int c = data[0];
215 : :
216 : : #define EMIT_LINE \
217 : : { \
218 : : buf[offset] = '\0'; \
219 : : int seq_len = data + 1 - data_start; \
220 : : seq_delivered_in_lines = seq + seq_len; \
221 : : last_char = c; \
222 : : ForwardStream(offset, buf, IsOrig()); \
223 : : offset = 0; \
224 : : return seq_len; \
225 : : }
226 : :
227 [ + + - + ]: 693881 : switch ( c ) {
228 : : case '\r':
229 : : // Look ahead for '\n'.
230 [ + + ][ + - ]: 12332 : if ( len > 1 && data[1] == '\n' )
231 : : {
232 : 12330 : --len; ++data;
233 : 12330 : last_char = c;
234 : 12330 : c = data[0];
235 : 12330 : EMIT_LINE
236 : : }
237 : :
238 [ + - ]: 2 : else if ( CR_LF_as_EOL & CR_as_EOL )
239 : 2 : EMIT_LINE
240 : :
241 : : else
242 : 0 : buf[offset++] = c;
243 : 0 : break;
244 : :
245 : : case '\n':
246 [ - + ]: 1 : if ( last_char == '\r' )
247 : : {
248 : 0 : --offset; // remove '\r'
249 : 0 : EMIT_LINE
250 : : }
251 : :
252 [ + - ]: 1 : else if ( CR_LF_as_EOL & LF_as_EOL )
253 : 1 : EMIT_LINE
254 : :
255 : : else
256 : : {
257 [ # # ]: 0 : if ( Conn()->FlagEvent(SINGULAR_LF) )
258 : 0 : Conn()->Weird("line_terminated_with_single_LF");
259 : 0 : buf[offset++] = c;
260 : : }
261 : 0 : break;
262 : :
263 : : case '\0':
264 [ # # ]: 0 : if ( flag_NULs )
265 : 0 : CheckNUL();
266 : : else
267 : 0 : buf[offset++] = c;
268 : 0 : break;
269 : :
270 : : default:
271 : 681548 : buf[offset++] = c;
272 : : break;
273 : : }
274 : :
275 [ - + ]: 681548 : if ( last_char == '\r' )
276 [ # # ]: 0 : if ( Conn()->FlagEvent(SINGULAR_CR) )
277 : 0 : Conn()->Weird("line_terminated_with_single_CR");
278 : :
279 : 681548 : last_char = c;
280 : : }
281 : :
282 : 12516 : return data - data_start;
283 : : }
284 : :
285 : 0 : void ContentLine_Analyzer::CheckNUL()
286 : : {
287 : : // If this is the first byte seen on this connection,
288 : : // and if the connection's state is PARTIAL, then we've
289 : : // intercepted a keep-alive, and shouldn't complain
290 : : // about it. Note that for PARTIAL connections, the
291 : : // starting sequence number is adjusted as though there
292 : : // had been an initial SYN, so we check for whether
293 : : // the connection has at most two bytes so far.
294 : :
295 : : TCP_Analyzer* tcp =
296 : 0 : static_cast<TCP_ApplicationAnalyzer*>(Parent())->TCP();
297 : :
298 [ # # ]: 0 : if ( tcp )
299 : : {
300 [ # # ]: 0 : TCP_Endpoint* endp = IsOrig() ? tcp->Orig() : tcp->Resp();
301 [ # # ][ # # ]: 0 : if ( endp->state == TCP_ENDPOINT_PARTIAL &&
[ # # ]
302 : : endp->LastSeq() - endp->StartSeq() <= 2 )
303 : : ; // Ignore it.
304 : : else
305 : : {
306 [ # # ]: 0 : if ( Conn()->FlagEvent(NUL_IN_LINE) )
307 : 0 : Conn()->Weird("NUL_in_line");
308 : 0 : flag_NULs = 0;
309 : : }
310 : : }
311 : 0 : }
312 : :
313 : 0 : void ContentLine_Analyzer::SkipBytesAfterThisLine(int length)
314 : : {
315 : : // This is a little complicated because Bro has to handle
316 : : // both CR and CRLF as a line break. When a line is delivered,
317 : : // it's possible that only a CR is seen, and we may not know
318 : : // if an LF is following until we see the next packet. If an
319 : : // LF follows, we should start skipping bytes *after* the LF.
320 : : // So we keep the skip as 'pending' until we see the next
321 : : // character in DoDeliver().
322 : :
323 [ # # ]: 0 : if ( last_char == '\r' )
324 : 0 : skip_pending = length;
325 : : else
326 : 0 : SkipBytes(length);
327 : 0 : }
328 : :
329 : 0 : void ContentLine_Analyzer::SkipBytes(int length)
330 : : {
331 : 0 : skip_pending = 0;
332 : 0 : seq_to_skip = SeqDelivered() + length;
333 [ + - ][ + - ]: 6 : }
334 : 3 :
|