/src/resiprocate/resip/stack/MsgHeaderScanner.cxx
Line | Count | Source |
1 | | #if defined(HAVE_CONFIG_H) |
2 | | #include "config.h" |
3 | | #endif |
4 | | |
5 | | #include <ctype.h> |
6 | | #include <limits.h> |
7 | | #include <stdio.h> |
8 | | #include "resip/stack/HeaderTypes.hxx" |
9 | | #include "resip/stack/SipMessage.hxx" |
10 | | #include "resip/stack/MsgHeaderScanner.hxx" |
11 | | #include "rutil/WinLeakCheck.hxx" |
12 | | #include "rutil/Logger.hxx" |
13 | | |
14 | 2.78k | #define RESIPROCATE_SUBSYSTEM Subsystem::SIP |
15 | | |
16 | | namespace resip |
17 | | { |
18 | | |
19 | | /////////////////////////////////////////////////////////////////////////////// |
20 | | // Any character could be used as the chunk terminating sentinel, as long as |
21 | | // it would otherwise be character category "other". The null character |
22 | | // was chosen because it is unlikely to occur naturally -- but it's OK if it |
23 | | // does. |
24 | | |
25 | | enum { chunkTermSentinelChar = '\0' }; |
26 | | |
27 | | enum CharCategoryEnum |
28 | | { |
29 | | ccChunkTermSentinel, |
30 | | ccOther, |
31 | | ccFieldName, |
32 | | ccWhitespace, |
33 | | ccColon, |
34 | | ccDoubleQuotationMark, |
35 | | ccLeftAngleBracket, |
36 | | ccRightAngleBracket, |
37 | | ccBackslash, |
38 | | ccComma, |
39 | | ccCarriageReturn, |
40 | | ccLineFeed, |
41 | | numCharCategories |
42 | | }; |
43 | | typedef char CharCategory; |
44 | | |
45 | | char* |
46 | | MsgHeaderScanner::allocateBuffer(size_t size) |
47 | 0 | { |
48 | 0 | return new char[size + MaxNumCharsChunkOverflow]; |
49 | 0 | } |
50 | | |
51 | | struct CharInfo |
52 | | { |
53 | | CharCategory category; |
54 | | MsgHeaderScanner::TextPropBitMask textPropBitMask; |
55 | | }; |
56 | | |
57 | | static CharInfo charInfoArray[UCHAR_MAX+1]; |
58 | | |
59 | | static inline int c2i(unsigned char c) |
60 | 1.98k | { |
61 | 1.98k | return static_cast<int>(c); |
62 | 1.98k | } |
63 | | |
64 | | static void initCharInfoArray() |
65 | 1 | { |
66 | 257 | for(unsigned int charIndex = 0; charIndex <= UCHAR_MAX; ++charIndex) |
67 | 256 | { |
68 | 256 | charInfoArray[charIndex].category = ccOther; |
69 | 256 | charInfoArray[charIndex].textPropBitMask = 0; |
70 | 256 | } |
71 | | |
72 | 1 | for(const char *charPtr = "abcdefghijklmnopqrstuvwxyz" |
73 | 1 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.!%*_+`'~"; |
74 | 73 | *charPtr; |
75 | 72 | ++charPtr) |
76 | 72 | { |
77 | 72 | charInfoArray[c2i(*charPtr)].category = ccFieldName; |
78 | 72 | } |
79 | | |
80 | 1 | charInfoArray[c2i(' ')].category = ccWhitespace; |
81 | 1 | charInfoArray[c2i('\t')].category = ccWhitespace; |
82 | 1 | charInfoArray[c2i(':')].category = ccColon; |
83 | 1 | charInfoArray[c2i('"')].category = ccDoubleQuotationMark; |
84 | 1 | charInfoArray[c2i('<')].category = ccLeftAngleBracket; |
85 | 1 | charInfoArray[c2i('>')].category = ccRightAngleBracket; |
86 | 1 | charInfoArray[c2i('\\')].category = ccBackslash; |
87 | 1 | charInfoArray[c2i(',')].category = ccComma; |
88 | 1 | charInfoArray[c2i('\r')].category = ccCarriageReturn; |
89 | 1 | charInfoArray[c2i('\n')].category = ccLineFeed; |
90 | | // Assert: "chunkTermSentinelChar"'s category is still the default "ccOther". |
91 | 1 | charInfoArray[c2i(chunkTermSentinelChar)].category = ccChunkTermSentinel; |
92 | | // Init text property bit masks. |
93 | 1 | charInfoArray[c2i('\r')].textPropBitMask = |
94 | 1 | MsgHeaderScanner::tpbmContainsLineBreak; |
95 | 1 | charInfoArray[c2i('\n')].textPropBitMask = |
96 | 1 | MsgHeaderScanner::tpbmContainsLineBreak; |
97 | 1 | charInfoArray[c2i(' ')].textPropBitMask = |
98 | 1 | MsgHeaderScanner::tpbmContainsWhitespace; |
99 | 1 | charInfoArray[c2i('\t')].textPropBitMask = |
100 | 1 | MsgHeaderScanner::tpbmContainsWhitespace; |
101 | 1 | charInfoArray[c2i('\\')].textPropBitMask = |
102 | 1 | MsgHeaderScanner::tpbmContainsBackslash; |
103 | 1 | charInfoArray[c2i('%')].textPropBitMask = |
104 | 1 | MsgHeaderScanner::tpbmContainsPercent; |
105 | 1 | charInfoArray[c2i(';')].textPropBitMask = |
106 | 1 | MsgHeaderScanner::tpbmContainsSemicolon; |
107 | 1 | charInfoArray[c2i('(')].textPropBitMask = |
108 | 1 | MsgHeaderScanner::tpbmContainsParen; |
109 | 1 | charInfoArray[c2i(')')].textPropBitMask = |
110 | 1 | MsgHeaderScanner::tpbmContainsParen; |
111 | 1 | } |
112 | | |
113 | | /////////////////////////////////////////////////////////////////////////////// |
114 | | // States marked '1' scan normal values. States marked 'N' scan multi-values. |
115 | | |
116 | | enum StateEnum |
117 | | { |
118 | | sMsgStart, |
119 | | sHalfLineBreakAtMsgStart, |
120 | | sScanStatusLine, |
121 | | sHalfLineBreakAfterStatusLine, |
122 | | sAfterLineBreakAfterStatusLine, |
123 | | sScanFieldName, |
124 | | sScanWhitespaceAfter1FieldName, |
125 | | sScanWhitespaceAfterNFieldName, |
126 | | sScanWhitespaceOr1Value, |
127 | | sScanWhitespaceOrNValue, |
128 | | sHalfLineBreakInWhitespaceBefore1Value, |
129 | | sHalfLineBreakInWhitespaceBeforeNValue, |
130 | | sAfterLineBreakInWhitespaceBefore1Value, |
131 | | sAfterLineBreakInWhitespaceBeforeNValue, |
132 | | sScan1Value, |
133 | | sScanNValue, |
134 | | sHalfLineBreakIn1Value, |
135 | | sHalfLineBreakInNValue, |
136 | | sAfterLineBreakIn1Value, |
137 | | sAfterLineBreakInNValue, |
138 | | sScanNValueInQuotes, |
139 | | sAfterEscCharInQuotesInNValue, |
140 | | sHalfLineBreakInQuotesInNValue, |
141 | | sAfterLineBreakInQuotesInNValue, |
142 | | sScanNValueInAngles, |
143 | | sHalfLineBreakInAnglesInNValue, |
144 | | sAfterLineBreakInAnglesInNValue, |
145 | | sHalfLineBreakAfterLineBreak, |
146 | | numStates |
147 | | }; |
148 | | |
149 | | typedef char State; |
150 | | |
151 | | // For each '1' state, the 'N' state is "deltaOfNStateFrom1State" larger. |
152 | | enum { deltaOfNStateFrom1State = 1 }; |
153 | | |
154 | | ///// |
155 | | |
156 | | enum TransitionActionEnum { |
157 | | taNone, |
158 | | taTermStatusLine, // The current character terminates the status |
159 | | // line. |
160 | | taTermFieldName, // The current character terminates a field name. |
161 | | // If the field supports multi-values, shift |
162 | | // the state machine into multi-value scanning. |
163 | | taBeyondEmptyValue, // The current character terminates an empty value. |
164 | | // Implies taStartText. |
165 | | taTermValueAfterLineBreak, |
166 | | // The previous two characters are a linebreak |
167 | | // terminating a value. Implies taStartText. |
168 | | taTermValue, // The current character terminates a value. |
169 | | taStartText, // The current character starts a text unit. |
170 | | // (The status line, a field name, or a value.) |
171 | | taEndHeader, // The current character mEnds_ the header. |
172 | | taChunkTermSentinel, // Either the current character terminates the |
173 | | // current chunk or it is an ordinary character. |
174 | | taError // The input is erroneous. |
175 | | }; |
176 | | typedef char TransitionAction; |
177 | | |
178 | | |
179 | | struct TransitionInfo |
180 | | { |
181 | | TransitionAction action; |
182 | | State nextState; |
183 | | }; |
184 | | |
185 | | static TransitionInfo stateMachine[numStates][numCharCategories]; |
186 | | |
187 | | inline void specTransition(State state, |
188 | | CharCategory charCategory, |
189 | | TransitionAction action, |
190 | | State nextState) |
191 | 474 | { |
192 | 474 | stateMachine[c2i(state)][c2i(charCategory)].action = action; |
193 | 474 | stateMachine[c2i(state)][c2i(charCategory)].nextState = nextState; |
194 | 474 | } |
195 | | |
196 | | static void specDefaultTransition(State state, |
197 | | TransitionAction action, |
198 | | State nextState) |
199 | 28 | { |
200 | 28 | for (int charCategory = 0; |
201 | 364 | charCategory < numCharCategories; |
202 | 336 | ++charCategory) |
203 | 336 | { |
204 | 336 | specTransition(state, charCategory, action, nextState); |
205 | 336 | } |
206 | 28 | specTransition(state, ccCarriageReturn, taError, state); |
207 | 28 | specTransition(state, ccLineFeed, taError, state); |
208 | 28 | specTransition(state, ccChunkTermSentinel, taChunkTermSentinel, state); |
209 | 28 | } |
210 | | |
211 | | static void specHalfLineBreakState(State halfLineBreakState, |
212 | | State afterLineBreakState) |
213 | 9 | { |
214 | 9 | specDefaultTransition(halfLineBreakState, taError, halfLineBreakState); |
215 | 9 | specTransition(halfLineBreakState, ccLineFeed, taNone, afterLineBreakState); |
216 | 9 | } |
217 | | |
218 | | |
219 | | // Single-value (1) scanning and multi-value (N) scanning involves several nearly |
220 | | // identical states. |
221 | | // "stateDelta" is either 0 or "deltaOfNStateFrom1State". |
222 | | |
223 | | static void specXValueStates(int stateDelta) |
224 | 2 | { |
225 | 2 | specDefaultTransition(sScanWhitespaceAfter1FieldName + stateDelta, |
226 | 2 | taError, |
227 | 2 | sScanWhitespaceAfter1FieldName + stateDelta); |
228 | 2 | specTransition(sScanWhitespaceAfter1FieldName + stateDelta, |
229 | 2 | ccWhitespace, |
230 | 2 | taNone, |
231 | 2 | sScanWhitespaceAfter1FieldName + stateDelta); |
232 | 2 | specTransition(sScanWhitespaceAfter1FieldName + stateDelta, |
233 | 2 | ccColon, |
234 | 2 | taNone, |
235 | 2 | sScanWhitespaceOr1Value + stateDelta); |
236 | 2 | specDefaultTransition(sScanWhitespaceOr1Value + stateDelta, |
237 | 2 | taStartText, |
238 | 2 | sScan1Value + stateDelta); |
239 | 2 | specTransition(sScanWhitespaceOr1Value + stateDelta, |
240 | 2 | ccWhitespace, |
241 | 2 | taNone, |
242 | 2 | sScanWhitespaceOr1Value + stateDelta); |
243 | 2 | if (stateDelta == deltaOfNStateFrom1State) |
244 | 1 | { |
245 | 1 | specTransition(sScanWhitespaceOr1Value + stateDelta, |
246 | 1 | ccComma, |
247 | 1 | taError, |
248 | 1 | sScanWhitespaceOr1Value + stateDelta); |
249 | 1 | specTransition(sScanWhitespaceOr1Value + stateDelta, |
250 | 1 | ccLeftAngleBracket, |
251 | 1 | taStartText, |
252 | 1 | sScanNValueInAngles); |
253 | 1 | specTransition(sScanWhitespaceOr1Value + stateDelta, |
254 | 1 | ccDoubleQuotationMark, |
255 | 1 | taStartText, |
256 | 1 | sScanNValueInQuotes); |
257 | 1 | } |
258 | 2 | specTransition(sScanWhitespaceOr1Value + stateDelta, |
259 | 2 | ccCarriageReturn, |
260 | 2 | taNone, |
261 | 2 | sHalfLineBreakInWhitespaceBefore1Value + stateDelta); |
262 | 2 | specHalfLineBreakState(sHalfLineBreakInWhitespaceBefore1Value + stateDelta, |
263 | 2 | sAfterLineBreakInWhitespaceBefore1Value + stateDelta); |
264 | 2 | specDefaultTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta, |
265 | 2 | taError, |
266 | 2 | sAfterLineBreakInWhitespaceBefore1Value + stateDelta); |
267 | 2 | specTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta, |
268 | 2 | ccFieldName, |
269 | 2 | taBeyondEmptyValue, |
270 | 2 | sScanFieldName); |
271 | 2 | specTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta, |
272 | 2 | ccWhitespace, |
273 | 2 | taNone, |
274 | 2 | sScanWhitespaceOr1Value + stateDelta); |
275 | 2 | specTransition(sAfterLineBreakInWhitespaceBefore1Value + stateDelta, |
276 | 2 | ccCarriageReturn, |
277 | 2 | taBeyondEmptyValue, |
278 | 2 | sHalfLineBreakAfterLineBreak); |
279 | 2 | specDefaultTransition(sScan1Value + stateDelta, |
280 | 2 | taNone, |
281 | 2 | sScan1Value + stateDelta); |
282 | 2 | if (stateDelta == deltaOfNStateFrom1State) |
283 | 1 | { |
284 | 1 | specTransition(sScan1Value + stateDelta, |
285 | 1 | ccComma, |
286 | 1 | taTermValue, |
287 | 1 | sScanWhitespaceOr1Value + stateDelta); |
288 | 1 | specTransition(sScan1Value + stateDelta, |
289 | 1 | ccLeftAngleBracket, |
290 | 1 | taNone, |
291 | 1 | sScanNValueInAngles); |
292 | 1 | specTransition(sScan1Value + stateDelta, |
293 | 1 | ccDoubleQuotationMark, |
294 | 1 | taNone, |
295 | 1 | sScanNValueInQuotes); |
296 | 1 | } |
297 | 2 | specTransition(sScan1Value + stateDelta, |
298 | 2 | ccCarriageReturn, |
299 | 2 | taNone, |
300 | 2 | sHalfLineBreakIn1Value + stateDelta); |
301 | 2 | specHalfLineBreakState(sHalfLineBreakIn1Value + stateDelta, |
302 | 2 | sAfterLineBreakIn1Value + stateDelta); |
303 | 2 | specDefaultTransition(sAfterLineBreakIn1Value + stateDelta, |
304 | 2 | taError, |
305 | 2 | sAfterLineBreakIn1Value + stateDelta); |
306 | 2 | specTransition(sAfterLineBreakIn1Value + stateDelta, |
307 | 2 | ccFieldName, |
308 | 2 | taTermValueAfterLineBreak, |
309 | 2 | sScanFieldName); |
310 | 2 | specTransition(sAfterLineBreakIn1Value + stateDelta, |
311 | 2 | ccWhitespace, |
312 | 2 | taNone, |
313 | 2 | sScan1Value + stateDelta); |
314 | 2 | specTransition(sAfterLineBreakIn1Value + stateDelta, |
315 | 2 | ccCarriageReturn, |
316 | 2 | taTermValueAfterLineBreak, |
317 | 2 | sHalfLineBreakAfterLineBreak); |
318 | 2 | } |
319 | | |
320 | | static void initStateMachine() |
321 | 1 | { |
322 | | // By convention, error transitions maintain the same state. |
323 | 1 | specDefaultTransition(sMsgStart, taStartText, sScanStatusLine); |
324 | 1 | specTransition(sMsgStart, |
325 | 1 | ccCarriageReturn, |
326 | 1 | taNone, |
327 | 1 | sHalfLineBreakAtMsgStart); |
328 | 1 | specTransition(sMsgStart, ccLineFeed, taError, sMsgStart); |
329 | 1 | specHalfLineBreakState(sHalfLineBreakAtMsgStart, sMsgStart); |
330 | 1 | specDefaultTransition(sScanStatusLine, taNone, sScanStatusLine); |
331 | 1 | specTransition(sScanStatusLine, |
332 | 1 | ccCarriageReturn, |
333 | 1 | taTermStatusLine, |
334 | 1 | sHalfLineBreakAfterStatusLine); |
335 | 1 | specHalfLineBreakState(sHalfLineBreakAfterStatusLine, |
336 | 1 | sAfterLineBreakAfterStatusLine); |
337 | 1 | specDefaultTransition(sAfterLineBreakAfterStatusLine, |
338 | 1 | taError, |
339 | 1 | sAfterLineBreakAfterStatusLine); |
340 | 1 | specTransition(sAfterLineBreakAfterStatusLine, |
341 | 1 | ccFieldName, |
342 | 1 | taStartText, |
343 | 1 | sScanFieldName); |
344 | 1 | specTransition(sAfterLineBreakAfterStatusLine, |
345 | 1 | ccWhitespace, |
346 | 1 | taError, |
347 | 1 | sAfterLineBreakAfterStatusLine); |
348 | 1 | specTransition(sAfterLineBreakAfterStatusLine, |
349 | 1 | ccCarriageReturn, |
350 | 1 | taNone, |
351 | 1 | sHalfLineBreakAfterLineBreak); |
352 | 1 | specDefaultTransition(sScanFieldName, taError, sScanFieldName); |
353 | 1 | specTransition(sScanFieldName, ccFieldName, taNone, sScanFieldName); |
354 | 1 | specTransition(sScanFieldName, |
355 | 1 | ccWhitespace, |
356 | 1 | taTermFieldName, |
357 | 1 | sScanWhitespaceAfter1FieldName); |
358 | 1 | specTransition(sScanFieldName, |
359 | 1 | ccColon, |
360 | 1 | taTermFieldName, |
361 | 1 | sScanWhitespaceOr1Value); |
362 | 1 | specXValueStates(0); |
363 | 1 | specXValueStates(deltaOfNStateFrom1State); |
364 | 1 | specDefaultTransition(sScanNValueInQuotes, taNone, sScanNValueInQuotes); |
365 | 1 | specTransition(sScanNValueInQuotes, |
366 | 1 | ccDoubleQuotationMark, |
367 | 1 | taNone, |
368 | 1 | sScanNValue); |
369 | 1 | specTransition(sScanNValueInQuotes, |
370 | 1 | ccBackslash, |
371 | 1 | taNone, |
372 | 1 | sAfterEscCharInQuotesInNValue); |
373 | 1 | specTransition(sScanNValueInQuotes, |
374 | 1 | ccCarriageReturn, |
375 | 1 | taNone, |
376 | 1 | sHalfLineBreakInQuotesInNValue); |
377 | 1 | specDefaultTransition(sAfterEscCharInQuotesInNValue, |
378 | 1 | taNone, |
379 | 1 | sScanNValueInQuotes); |
380 | 1 | specHalfLineBreakState(sHalfLineBreakInQuotesInNValue, |
381 | 1 | sAfterLineBreakInQuotesInNValue); |
382 | 1 | specDefaultTransition(sAfterLineBreakInQuotesInNValue, |
383 | 1 | taError, |
384 | 1 | sAfterLineBreakInQuotesInNValue); |
385 | 1 | specTransition(sAfterLineBreakInQuotesInNValue, |
386 | 1 | ccWhitespace, |
387 | 1 | taNone, |
388 | 1 | sScanNValueInQuotes); |
389 | 1 | specDefaultTransition(sScanNValueInAngles, taNone, sScanNValueInAngles); |
390 | 1 | specTransition(sScanNValueInAngles, |
391 | 1 | ccRightAngleBracket, |
392 | 1 | taNone, |
393 | 1 | sScanNValue); |
394 | 1 | specTransition(sScanNValueInAngles, |
395 | 1 | ccCarriageReturn, |
396 | 1 | taNone, |
397 | 1 | sHalfLineBreakInAnglesInNValue); |
398 | 1 | specHalfLineBreakState(sHalfLineBreakInAnglesInNValue, |
399 | 1 | sAfterLineBreakInAnglesInNValue); |
400 | 1 | specDefaultTransition(sAfterLineBreakInAnglesInNValue, |
401 | 1 | taError, |
402 | 1 | sAfterLineBreakInAnglesInNValue); |
403 | 1 | specTransition(sAfterLineBreakInAnglesInNValue, |
404 | 1 | ccWhitespace, |
405 | 1 | taNone, |
406 | 1 | sScanNValueInAngles); |
407 | 1 | specHalfLineBreakState(sHalfLineBreakAfterLineBreak, sMsgStart); |
408 | | |
409 | | // Most half-line-break states do nothing when they read a line feed, |
410 | | // but sHalfLineBreakAfterLineBreak must end the message header scanning. |
411 | | |
412 | 1 | specTransition(sHalfLineBreakAfterLineBreak, |
413 | 1 | ccLineFeed, |
414 | 1 | taEndHeader, |
415 | 1 | sMsgStart); // Arbitrary but possibly handy. |
416 | 1 | } |
417 | | |
418 | | // Debug follows |
419 | | #if defined(RESIP_MSG_HEADER_SCANNER_DEBUG) |
420 | | |
421 | | static void printText(const char * text, |
422 | | unsigned int textLength) |
423 | | { |
424 | | const char *charPtr = text; |
425 | | for (unsigned int counter = 0; counter < textLength; ++charPtr, ++counter) |
426 | | { |
427 | | char c = *charPtr; |
428 | | switch (c) |
429 | | { |
430 | | case '\\': printf("\\\\"); |
431 | | break; |
432 | | case '\r': printf("\\r"); |
433 | | break; |
434 | | case '\n': printf("\\n"); |
435 | | break; |
436 | | case '\t': printf("\\t"); |
437 | | break; |
438 | | case '\0': printf("\\0"); |
439 | | break; |
440 | | default: putchar(c); |
441 | | } |
442 | | } |
443 | | } |
444 | | |
445 | | static const char * |
446 | | categorySymbol(CharCategory c) |
447 | | { |
448 | | switch(c) |
449 | | { |
450 | | case ccChunkTermSentinel: return "TERM"; |
451 | | case ccOther: return "*"; |
452 | | case ccFieldName: return "FName"; |
453 | | case ccWhitespace: return "WS"; |
454 | | case ccColon: return "\\\":\\\""; |
455 | | case ccDoubleQuotationMark: return "\\\""; |
456 | | case ccLeftAngleBracket: return "\\\"<\\\""; |
457 | | case ccRightAngleBracket: return "\\\">\\\""; |
458 | | case ccBackslash: return "\\\"\\\\\\\""; |
459 | | case ccComma: return "\\\",\\\""; |
460 | | case ccCarriageReturn: return "CR"; |
461 | | case ccLineFeed: return "LF"; |
462 | | } |
463 | | return "??CC??"; |
464 | | } |
465 | | |
466 | | static const char * |
467 | | categoryName(CharCategory c) |
468 | | { |
469 | | switch(c) |
470 | | { |
471 | | case ccChunkTermSentinel: return "ccChunkTermSentinel"; |
472 | | case ccOther: return "ccOther"; |
473 | | case ccFieldName: return "ccFieldName"; |
474 | | case ccWhitespace: return "ccWhitespace"; |
475 | | case ccColon: return "ccColon"; |
476 | | case ccDoubleQuotationMark: return "ccDoubleQuotationMark"; |
477 | | case ccLeftAngleBracket: return "ccLeftAngleBracket"; |
478 | | case ccRightAngleBracket: return "ccRightAngleBracket"; |
479 | | case ccBackslash: return "ccBackslash"; |
480 | | case ccComma: return "ccComma"; |
481 | | case ccCarriageReturn: return "ccCarriageReturn"; |
482 | | case ccLineFeed: return "ccLineFeed"; |
483 | | } |
484 | | return "UNKNOWNCC"; |
485 | | } |
486 | | |
487 | | static const char * |
488 | | cleanName(const char * name) |
489 | | { |
490 | | // Remove leading type-noise from name |
491 | | static char *leaders[] = { |
492 | | "cc", |
493 | | "s", |
494 | | "taChunkTerm", // hack to make ChunkTermSentinel smaller |
495 | | "ta" |
496 | | }; |
497 | | const int nLeaders = sizeof(leaders)/sizeof(*leaders); |
498 | | int offset = 0; |
499 | | for(int i = 0 ; i < nLeaders ; i++) |
500 | | { |
501 | | unsigned int l = strlen(leaders[i]); |
502 | | if (strstr(name,leaders[i]) == name && |
503 | | strlen(name) > l && |
504 | | isupper(static_cast< unsigned char >(name[l]))) |
505 | | { |
506 | | offset = l; |
507 | | break; |
508 | | } |
509 | | } |
510 | | return &name[offset]; |
511 | | } |
512 | | |
513 | | static const char * |
514 | | stateName(State state) |
515 | | { |
516 | | const char *stateName; |
517 | | switch (state) |
518 | | { |
519 | | case sMsgStart: |
520 | | stateName = "sMsgStart"; |
521 | | break; |
522 | | case sHalfLineBreakAtMsgStart: |
523 | | stateName = "sHalfLineBreakAtMsgStart"; |
524 | | break; |
525 | | case sScanStatusLine: |
526 | | stateName = "sScanStatusLine"; |
527 | | break; |
528 | | case sHalfLineBreakAfterStatusLine: |
529 | | stateName = "sHalfLineBreakAfterStatusLine"; |
530 | | break; |
531 | | case sAfterLineBreakAfterStatusLine: |
532 | | stateName = "sAfterLineBreakAfterStatusLine"; |
533 | | break; |
534 | | case sScanFieldName: |
535 | | stateName = "sScanFieldName"; |
536 | | break; |
537 | | case sScanWhitespaceAfter1FieldName: |
538 | | stateName = "sScanWhitespaceAfter1FieldName"; |
539 | | break; |
540 | | case sScanWhitespaceAfterNFieldName: |
541 | | stateName = "sScanWhitespaceAfterNFieldName"; |
542 | | break; |
543 | | case sScanWhitespaceOr1Value: |
544 | | stateName = "sScanWhitespaceOr1Value"; |
545 | | break; |
546 | | case sScanWhitespaceOrNValue: |
547 | | stateName = "sScanWhitespaceOrNValue"; |
548 | | break; |
549 | | case sHalfLineBreakInWhitespaceBefore1Value: |
550 | | stateName = "sHalfLineBreakInWhitespaceBefore1Value"; |
551 | | break; |
552 | | case sHalfLineBreakInWhitespaceBeforeNValue: |
553 | | stateName = "sHalfLineBreakInWhitespaceBeforeNValue"; |
554 | | break; |
555 | | case sAfterLineBreakInWhitespaceBefore1Value: |
556 | | stateName = "sAfterLineBreakInWhitespaceBefore1Value"; |
557 | | break; |
558 | | case sAfterLineBreakInWhitespaceBeforeNValue: |
559 | | stateName = "sAfterLineBreakInWhitespaceBeforeNValue"; |
560 | | break; |
561 | | case sScan1Value: |
562 | | stateName = "sScan1Value"; |
563 | | break; |
564 | | case sScanNValue: |
565 | | stateName = "sScanNValue"; |
566 | | break; |
567 | | case sHalfLineBreakIn1Value: |
568 | | stateName = "sHalfLineBreakIn1Value"; |
569 | | break; |
570 | | case sHalfLineBreakInNValue: |
571 | | stateName = "sHalfLineBreakInNValue"; |
572 | | break; |
573 | | case sAfterLineBreakIn1Value: |
574 | | stateName = "sAfterLineBreakIn1Value"; |
575 | | break; |
576 | | case sAfterLineBreakInNValue: |
577 | | stateName = "sAfterLineBreakInNValue"; |
578 | | break; |
579 | | case sScanNValueInQuotes: |
580 | | stateName = "sScanNValueInQuotes"; |
581 | | break; |
582 | | case sAfterEscCharInQuotesInNValue: |
583 | | stateName = "sAfterEscCharInQuotesInNValue"; |
584 | | break; |
585 | | case sHalfLineBreakInQuotesInNValue: |
586 | | stateName = "sHalfLineBreakInQuotesInNValue"; |
587 | | break; |
588 | | case sAfterLineBreakInQuotesInNValue: |
589 | | stateName = "sAfterLineBreakInQuotesInNValue"; |
590 | | break; |
591 | | case sScanNValueInAngles: |
592 | | stateName = "sScanNValueInAngles"; |
593 | | break; |
594 | | case sHalfLineBreakInAnglesInNValue: |
595 | | stateName = "sHalfLineBreakInAnglesInNValue"; |
596 | | break; |
597 | | case sAfterLineBreakInAnglesInNValue: |
598 | | stateName = "sAfterLineBreakInAnglesInNValue"; |
599 | | break; |
600 | | case sHalfLineBreakAfterLineBreak: |
601 | | stateName = "sHalfLineBreakAfterLineBreak"; |
602 | | break; |
603 | | default: |
604 | | stateName = "<unknown>"; |
605 | | }//switch |
606 | | return stateName; |
607 | | } |
608 | | |
609 | | static const char * |
610 | | trActionName(TransitionAction transitionAction) |
611 | | { |
612 | | const char *transitionActionName; |
613 | | switch (transitionAction) |
614 | | { |
615 | | case taNone: |
616 | | transitionActionName = "taNone"; |
617 | | break; |
618 | | case taTermStatusLine: |
619 | | transitionActionName = "taTermStatusLine"; |
620 | | break; |
621 | | case taTermFieldName: |
622 | | transitionActionName = "taTermFieldName"; |
623 | | break; |
624 | | case taBeyondEmptyValue: |
625 | | transitionActionName = "taBeyondEmptyValue"; |
626 | | break; |
627 | | case taTermValueAfterLineBreak: |
628 | | transitionActionName = "taTermValueAfterLineBreak"; |
629 | | break; |
630 | | case taTermValue: |
631 | | transitionActionName = "taTermValue"; |
632 | | break; |
633 | | case taStartText: |
634 | | transitionActionName = "taStartText"; |
635 | | break; |
636 | | case taEndHeader: |
637 | | transitionActionName = "taEndHeader"; |
638 | | break; |
639 | | case taChunkTermSentinel: |
640 | | transitionActionName = "taChunkTermSentinel"; |
641 | | break; |
642 | | case taError: |
643 | | transitionActionName = "taError"; |
644 | | break; |
645 | | default: |
646 | | transitionActionName = "<unknown>"; |
647 | | } |
648 | | return transitionActionName; |
649 | | } |
650 | | |
651 | | static void |
652 | | printStateTransition(State state, |
653 | | char character, |
654 | | TransitionAction transitionAction) |
655 | | { |
656 | | printf(" %s['", cleanName(stateName(state))); |
657 | | printText(&character, 1); |
658 | | printf("']: %s\n", cleanName(trActionName(transitionAction))); |
659 | | } |
660 | | #if !defined(RESIP_MSG_HEADER_SCANNER_DEBUG) |
661 | | static const char* stateName(const char*) |
662 | | { return "RECOMPILE_WITH_SCANNER_DEBUG"; } |
663 | | static const char* trActionName(const char*) |
664 | | { return stateName(0); } |
665 | | #endif |
666 | | /// START OF MEMBER METHODS |
667 | | |
668 | | |
669 | | |
670 | | int |
671 | | MsgHeaderScanner::dumpStateMachine(int fd) |
672 | | { |
673 | | FILE *fp = fdopen(fd,"w"); |
674 | | if (!fp) |
675 | | { |
676 | | fprintf(stderr,"MsgHeaderScanner:: unable to open output file\n"); |
677 | | return -1; |
678 | | } |
679 | | // Force instance so things are initialized -- YUCK! |
680 | | MsgHeaderScanner scanner;(void)scanner; |
681 | | fprintf(fp,"digraph MsgHeaderScannerFSM {\n"); |
682 | | fprintf(fp,"\tnode[shape=record\n\t\tfontsize=8\n\t\tfontname=\"Helvetica\"\n\t]\n"); |
683 | | fprintf(fp,"\tedge [ fontsize=6 fontname=\"Helvetica\"]\n"); |
684 | | |
685 | | fprintf(fp,"\tgraph [ ratio=0.8\n\t\tfontsize=6 compound=true ]"); |
686 | | for(int state = 0 ; state < numStates; ++state) |
687 | | { |
688 | | fprintf(fp, |
689 | | " %s [ label = \"%d|%s\" ]\n", |
690 | | cleanName(stateName(state)), |
691 | | state, |
692 | | cleanName(stateName(state)) |
693 | | ); |
694 | | for(int category = 0 ; category < numCharCategories; ++category) |
695 | | { |
696 | | // Skip Verbose Error or Empty Transitions |
697 | | if (stateMachine[state][category].nextState == state && |
698 | | (stateMachine[state][category].action == taError || |
699 | | stateMachine[state][category].action == taNone |
700 | | )) continue; |
701 | | |
702 | | fprintf(fp, |
703 | | " %s -> %s [label=\"%s\\n%s\" ]\n", |
704 | | cleanName(stateName(state)), |
705 | | cleanName(stateName(stateMachine[state][category].nextState)), |
706 | | categorySymbol(category), |
707 | | cleanName(trActionName(stateMachine[state][category].action))); |
708 | | } |
709 | | fprintf(fp,"\n"); |
710 | | } |
711 | | fprintf(fp,"}\n"); |
712 | | |
713 | | return 0; |
714 | | } |
715 | | |
716 | | #endif //defined(RESIP_MSG_HEADER_SCANNER_DEBUG) |
717 | | |
718 | | |
719 | | |
720 | | #if defined(RESIP_MSG_HEADER_SCANNER_DEBUG) |
721 | | |
722 | | static const char *const multiValuedFieldNameArray[] = { |
723 | | "allow-events", |
724 | | "accept-encoding", |
725 | | "accept-language", |
726 | | "allow", |
727 | | "content-language", |
728 | | "proxy-require", |
729 | | "require", |
730 | | "supported", |
731 | | "subscription-state", |
732 | | "unsupported", |
733 | | "security-client", |
734 | | "security-server", |
735 | | "security-verify", |
736 | | "accept", |
737 | | "call-info", |
738 | | "alert-info", |
739 | | "error-info", |
740 | | "record-route", |
741 | | "route", |
742 | | "contact", |
743 | | "authorization", |
744 | | "proxy-authenticate", |
745 | | "proxy-authorization", |
746 | | "www-authenticate", |
747 | | "via", |
748 | | 0 |
749 | | }; |
750 | | |
751 | | extern |
752 | | void |
753 | | lookupMsgHeaderFieldInfo( |
754 | | char * fieldName, //inout |
755 | | unsigned int *fieldNameLength, //inout |
756 | | MsgHeaderScanner::TextPropBitMask fieldNameTextPropBitMask, |
757 | | int *fieldKind, //out |
758 | | bool *isMultiValueAllowed) //out |
759 | | { |
760 | | *isMultiValueAllowed = false; |
761 | | const char *const *multiValuedFieldNamePtr = multiValuedFieldNameArray; |
762 | | for (;;) |
763 | | { |
764 | | const char *multiValuedFieldName = *multiValuedFieldNamePtr; |
765 | | if (!multiValuedFieldName) |
766 | | { |
767 | | break; |
768 | | } |
769 | | if (strncmp(fieldName, multiValuedFieldName, *fieldNameLength) == 0) |
770 | | { |
771 | | *isMultiValueAllowed = true; |
772 | | break; |
773 | | } |
774 | | ++multiValuedFieldNamePtr; |
775 | | }//for |
776 | | } |
777 | | |
778 | | static |
779 | | bool |
780 | | processMsgHeaderStatusLine( |
781 | | SipMessage * msg, |
782 | | char * lineText, |
783 | | unsigned int lineTextLength, |
784 | | MsgHeaderScanner::TextPropBitMask lineTextPropBitMask) |
785 | | { |
786 | | printf("status line: "); |
787 | | printText(lineText, lineTextLength); |
788 | | printf("\n"); |
789 | | return true; |
790 | | } |
791 | | |
792 | | static |
793 | | void |
794 | | processMsgHeaderFieldNameAndValue( |
795 | | SipMessage * msg, |
796 | | int fieldKind, |
797 | | const char * fieldName, |
798 | | unsigned int fieldNameLength, |
799 | | char * valueText, |
800 | | unsigned int valueTextLength, |
801 | | MsgHeaderScanner::TextPropBitMask valueTextPropBitMask) |
802 | | { |
803 | | printText(fieldName, fieldNameLength); |
804 | | printf(": [[[["); |
805 | | printText(valueText, valueTextLength); |
806 | | printf("]]]]\n"); |
807 | | } |
808 | | |
809 | | #else //!defined(RESIP_MSG_HEADER_SCANNER_DEBUG) } { |
810 | | |
811 | | |
812 | | // Determine a field's kind and whether it allows (comma separated) multi-values. |
813 | | // "fieldName" is not empty and contains only legal characters. |
814 | | // The text in "fieldName" may be canonicalized (eg, translating % escapes), |
815 | | // including shrinking it if necessary. |
816 | | |
817 | | inline void |
818 | | lookupMsgHeaderFieldInfo(char * fieldName, |
819 | | unsigned int *fieldNameLength, |
820 | | MsgHeaderScanner::TextPropBitMask fieldNameTextPropBitMask, |
821 | | int *fieldKind, |
822 | | bool *isMultiValueAllowed) |
823 | 1.28k | { |
824 | | //.jacob. Don't ignore fieldNameTextPropBitMask. |
825 | 1.28k | *fieldKind = Headers::getType(fieldName, *fieldNameLength); |
826 | 1.28k | *isMultiValueAllowed = |
827 | 1.28k | Headers::isCommaTokenizing(static_cast<Headers::Type>(*fieldKind)); |
828 | 1.28k | } |
829 | | |
830 | | |
831 | | // "lineText" contains no carriage returns and no line feeds. |
832 | | // Return true on success, false on failure. |
833 | | |
834 | | inline bool |
835 | | processMsgHeaderStatusLine(SipMessage * msg, |
836 | | char * lineText, |
837 | | unsigned int lineTextLength, |
838 | | MsgHeaderScanner::TextPropBitMask lineTextPropBitMask) |
839 | 575 | { |
840 | | //.jacob. Don't ignore valueTextPropBitMask, and don't always return true. |
841 | 575 | msg->setStartLine(lineText, lineTextLength); |
842 | 575 | return true; |
843 | 575 | } |
844 | | |
845 | | // This function is called once for a field with one value. (The value could be |
846 | | // several values, but separated by something other than commas.) |
847 | | // This function is called once for a field with 0 comma-separated values, with |
848 | | // an empty value. |
849 | | // This function is called N times for a field with N comma-separated values, |
850 | | // but with the same value of "fieldName" each time. |
851 | | // "fieldName" is not empty and contains only legal characters. |
852 | | // "valueText" may be empty, has no leading whitespace, may contain trailing |
853 | | // whitespace, contains carriage returns and line feeds only in correct pairs |
854 | | // and followed by whitespace, and, if the field is multi-valued, contains |
855 | | // balanced '<'/'>' and '"' pairs, contains ',' only within '<'/'>' or '"' |
856 | | // pairs, and respects '\\'s within '"' pairs. |
857 | | // The text in "valueText" may be canonicalized (eg, translating % escapes), |
858 | | // including shrinking it if necessary. |
859 | | |
860 | | inline void |
861 | | processMsgHeaderFieldNameAndValue(SipMessage * msg, |
862 | | int fieldKind, |
863 | | const char * fieldName, |
864 | | unsigned int fieldNameLength, |
865 | | char * valueText, |
866 | | unsigned int valueTextLength, |
867 | | MsgHeaderScanner::TextPropBitMask valueTextPropBitMask) |
868 | 565k | { |
869 | | //.jacob. Don't ignore valueTextPropBitMask, particularly for '\r' & '\n'. |
870 | 565k | msg->addHeader(static_cast<Headers::Type>(fieldKind), |
871 | 565k | fieldName, |
872 | 565k | fieldNameLength, |
873 | 565k | valueText, |
874 | 565k | valueTextLength); |
875 | 565k | } |
876 | | |
877 | | #endif //!defined(RESIP_MSG_HEADER_SCANNER_DEBUG) } |
878 | | |
879 | | bool MsgHeaderScanner::mInitialized = false; |
880 | | |
881 | | MsgHeaderScanner::MsgHeaderScanner() |
882 | 6.11k | { |
883 | 6.11k | if (!mInitialized) |
884 | 1 | { |
885 | 1 | mInitialized = true; |
886 | 1 | initialize(); |
887 | 1 | } |
888 | 6.11k | } |
889 | | |
890 | | void |
891 | | MsgHeaderScanner::prepareForMessage(SipMessage * msg) |
892 | 6.11k | { |
893 | 6.11k | mMsg = msg; |
894 | 6.11k | mState = sMsgStart; |
895 | 6.11k | mPrevScanChunkNumSavedTextChars = 0; |
896 | 6.11k | mNumHeaders=0; |
897 | 6.11k | } |
898 | | |
899 | | void |
900 | | MsgHeaderScanner::prepareForFrag(SipMessage * msg, bool hasStartLine) |
901 | 0 | { |
902 | 0 | mMsg = msg; |
903 | 0 | if (hasStartLine) |
904 | 0 | { |
905 | 0 | mState = sMsgStart; |
906 | 0 | } |
907 | 0 | else |
908 | 0 | { |
909 | 0 | mState = sAfterLineBreakAfterStatusLine; |
910 | 0 | } |
911 | 0 | mPrevScanChunkNumSavedTextChars = 0; |
912 | 0 | mNumHeaders=0; |
913 | 0 | } |
914 | | |
915 | | MsgHeaderScanner::ScanChunkResult |
916 | | MsgHeaderScanner::scanChunk(char * chunk, |
917 | | unsigned int chunkLength, |
918 | | char ** unprocessedCharPtr) |
919 | 6.11k | { |
920 | 6.11k | MsgHeaderScanner::ScanChunkResult result; |
921 | 6.11k | CharInfo* localCharInfoArray = charInfoArray; |
922 | 6.11k | TransitionInfo (*localStateMachine)[numCharCategories] = stateMachine; |
923 | 6.11k | State localState = mState; |
924 | 6.11k | char *charPtr = chunk + mPrevScanChunkNumSavedTextChars; |
925 | 6.11k | char *termCharPtr = chunk + chunkLength; |
926 | 6.11k | char saveChunkTermChar = *termCharPtr; |
927 | 6.11k | *termCharPtr = chunkTermSentinelChar; |
928 | 6.11k | char *textStartCharPtr; |
929 | 6.11k | MsgHeaderScanner::TextPropBitMask localTextPropBitMask = mTextPropBitMask; |
930 | 6.11k | if (mPrevScanChunkNumSavedTextChars == 0) |
931 | 6.11k | { |
932 | 6.11k | textStartCharPtr = 0; |
933 | 6.11k | } |
934 | 0 | else |
935 | 0 | { |
936 | 0 | textStartCharPtr = chunk; |
937 | 0 | } |
938 | 6.11k | --charPtr; // The loop starts by advancing "charPtr", so pre-adjust it. |
939 | 6.11k | for (;;) |
940 | 55.6M | { |
941 | | // BEGIN message header character scan block BEGIN |
942 | | // The code in this block is executed once per message header character. |
943 | | // This entire file is designed specifically to minimize this block's size. |
944 | 55.6M | ++charPtr; |
945 | 55.6M | CharInfo *charInfo = &localCharInfoArray[((unsigned char) (*charPtr))]; |
946 | 55.6M | CharCategory charCategory = charInfo->category; |
947 | 55.6M | localTextPropBitMask |= charInfo->textPropBitMask; |
948 | 61.8M | determineTransitionFromCharCategory: |
949 | 61.8M | TransitionInfo *transitionInfo = |
950 | 61.8M | &(localStateMachine[(unsigned)localState][(size_t)charCategory]); |
951 | 61.8M | TransitionAction transitionAction = transitionInfo->action; |
952 | | #if defined(RESIP_MSG_HEADER_SCANNER_DEBUG) |
953 | | printStateTransition(localState, *charPtr, transitionAction); |
954 | | #endif |
955 | 61.8M | localState = transitionInfo->nextState; |
956 | 61.8M | if (transitionAction == taNone) continue; |
957 | | // END message header character scan block END |
958 | | // The loop remainder is executed about 4-5 times per message header line. |
959 | 7.34M | switch (transitionAction) |
960 | 7.34M | { |
961 | 575 | case taTermStatusLine: |
962 | 575 | if (!processMsgHeaderStatusLine(mMsg, |
963 | 575 | textStartCharPtr, |
964 | 575 | (unsigned int)(charPtr - textStartCharPtr), |
965 | 575 | localTextPropBitMask)) |
966 | 0 | { |
967 | 0 | result = MsgHeaderScanner::scrError; |
968 | 0 | *unprocessedCharPtr = charPtr; |
969 | 0 | goto endOfFunction; |
970 | 0 | } |
971 | 575 | textStartCharPtr = 0; |
972 | 575 | break; |
973 | 1.28k | case taTermFieldName: |
974 | 1.28k | { |
975 | 1.28k | mFieldNameLength = (unsigned int)(charPtr - textStartCharPtr); |
976 | 1.28k | bool isMultiValueAllowed; |
977 | 1.28k | lookupMsgHeaderFieldInfo(textStartCharPtr, |
978 | 1.28k | &mFieldNameLength, |
979 | 1.28k | localTextPropBitMask, |
980 | 1.28k | &mFieldKind, |
981 | 1.28k | &isMultiValueAllowed); |
982 | 1.28k | mFieldName = textStartCharPtr; |
983 | 1.28k | textStartCharPtr = 0; |
984 | 1.28k | if (isMultiValueAllowed) |
985 | 432 | { |
986 | 432 | localState += deltaOfNStateFrom1State; |
987 | 432 | } |
988 | 1.28k | } |
989 | 1.28k | break; |
990 | 737 | case taBeyondEmptyValue: |
991 | 737 | processMsgHeaderFieldNameAndValue(mMsg, |
992 | 737 | mFieldKind, |
993 | 737 | mFieldName, |
994 | 737 | mFieldNameLength, |
995 | 737 | 0, |
996 | 737 | 0, |
997 | 737 | 0); |
998 | 737 | ++mNumHeaders; |
999 | 737 | goto performStartTextAction; |
1000 | 412 | case taTermValueAfterLineBreak: |
1001 | 412 | processMsgHeaderFieldNameAndValue(mMsg, |
1002 | 412 | mFieldKind, |
1003 | 412 | mFieldName, |
1004 | 412 | mFieldNameLength, |
1005 | 412 | textStartCharPtr, |
1006 | 412 | (unsigned int)((charPtr - textStartCharPtr) - 2), |
1007 | 412 | localTextPropBitMask); //^:CRLF |
1008 | 412 | ++mNumHeaders; |
1009 | 412 | goto performStartTextAction; |
1010 | 564k | case taTermValue: |
1011 | 564k | processMsgHeaderFieldNameAndValue(mMsg, |
1012 | 564k | mFieldKind, |
1013 | 564k | mFieldName, |
1014 | 564k | mFieldNameLength, |
1015 | 564k | textStartCharPtr, |
1016 | 564k | (unsigned int)(charPtr - textStartCharPtr), |
1017 | 564k | localTextPropBitMask); |
1018 | 564k | textStartCharPtr = 0; |
1019 | 564k | ++mNumHeaders; |
1020 | 564k | break; |
1021 | 570k | case taStartText: |
1022 | 572k | performStartTextAction: |
1023 | 572k | textStartCharPtr = charPtr; |
1024 | 572k | localTextPropBitMask = 0; |
1025 | 572k | break; |
1026 | 153 | case taEndHeader: |
1027 | | // textStartCharPtr is not 0. Not currently relevant. |
1028 | 153 | result = MsgHeaderScanner::scrEnd; |
1029 | 153 | *unprocessedCharPtr = charPtr + 1; // The current char is processed. |
1030 | 153 | goto endOfFunction; |
1031 | 0 | break; |
1032 | 6.20M | case taChunkTermSentinel: |
1033 | 6.20M | if (charPtr == termCharPtr) |
1034 | 3.17k | { |
1035 | | // The chunk has been consumed. Save some state and request another. |
1036 | 3.17k | mState = localState; |
1037 | 3.17k | if (textStartCharPtr == 0) |
1038 | 144 | { |
1039 | 144 | mPrevScanChunkNumSavedTextChars = 0; |
1040 | 144 | } |
1041 | 3.03k | else |
1042 | 3.03k | { |
1043 | 3.03k | mPrevScanChunkNumSavedTextChars = (unsigned int)(termCharPtr - textStartCharPtr); |
1044 | 3.03k | } |
1045 | 3.17k | mTextPropBitMask = localTextPropBitMask; |
1046 | 3.17k | result = MsgHeaderScanner::scrNextChunk; |
1047 | 3.17k | *unprocessedCharPtr = termCharPtr - mPrevScanChunkNumSavedTextChars; |
1048 | 3.17k | goto endOfFunction; |
1049 | 3.17k | } |
1050 | 6.19M | else |
1051 | 6.19M | { |
1052 | | // The character is not the sentinel. Treat it like any other. |
1053 | 6.19M | charCategory = ccOther; |
1054 | 6.19M | goto determineTransitionFromCharCategory; |
1055 | 6.19M | } |
1056 | 0 | break; |
1057 | 2.78k | default: |
1058 | 2.78k | result = MsgHeaderScanner::scrError; |
1059 | 2.78k | *unprocessedCharPtr = charPtr; |
1060 | 2.78k | goto endOfFunction; |
1061 | 7.34M | }//switch |
1062 | 7.34M | }//for |
1063 | 6.11k | endOfFunction: |
1064 | 6.11k | *termCharPtr = saveChunkTermChar; |
1065 | | |
1066 | 6.11k | if (result == MsgHeaderScanner::scrError && genericLogCheckLevel(resip::Log::Debug, RESIPROCATE_SUBSYSTEM)) |
1067 | 0 | { |
1068 | 0 | const char* failedChar = *unprocessedCharPtr; |
1069 | 0 | const char* lineBegin = failedChar; |
1070 | 0 | while (lineBegin > chunk) |
1071 | 0 | { |
1072 | 0 | char c = *(lineBegin - 1); |
1073 | 0 | if (!isprint(static_cast< unsigned char >(c)) && c != '\t') |
1074 | 0 | break; |
1075 | 0 | --lineBegin; |
1076 | 0 | } |
1077 | 0 | const char* lineEnd = failedChar; |
1078 | 0 | while (lineEnd < termCharPtr) |
1079 | 0 | { |
1080 | 0 | char c = *lineEnd; |
1081 | 0 | if (!isprint(static_cast< unsigned char >(c)) && c != '\t') |
1082 | 0 | break; |
1083 | 0 | ++lineEnd; |
1084 | 0 | } |
1085 | |
|
1086 | 0 | resip::Log::Guard lg(resip::Log::Debug, RESIPROCATE_SUBSYSTEM, __FILE__, __LINE__, __func__); |
1087 | 0 | EncodeStream& strm = lg.asStream(); |
1088 | 0 | strm << "Message header scanning failed at position " << (failedChar - chunk) << '\n'; |
1089 | 0 | strm.write(lineBegin, lineEnd - lineBegin); |
1090 | 0 | strm.put('\n'); |
1091 | 0 | static const char filler[] = " "; |
1092 | 0 | unsigned int n = failedChar - lineBegin; |
1093 | 0 | while (n > sizeof(filler) - 1) |
1094 | 0 | { |
1095 | 0 | strm.write(filler, sizeof(filler) - 1); |
1096 | 0 | n -= sizeof(filler) - 1; |
1097 | 0 | } |
1098 | 0 | strm.write(filler, n); |
1099 | 0 | strm << "^ (character codes:"; |
1100 | 0 | char buf[20]; |
1101 | 0 | char* p = buf; |
1102 | 0 | auto print_hex = [&p](char c) |
1103 | 0 | { |
1104 | 0 | static const char hex_charset[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
1105 | 0 | *p++ = hex_charset[(static_cast< unsigned char >(c) >> 4u) & 0x0F]; |
1106 | 0 | *p++ = hex_charset[static_cast< unsigned char >(c) & 0x0F]; |
1107 | 0 | }; |
1108 | 0 | if ((failedChar - chunk) >= 2) |
1109 | 0 | { |
1110 | 0 | *p++ = ' '; |
1111 | 0 | print_hex(*(failedChar - 2)); |
1112 | 0 | } |
1113 | 0 | if ((failedChar - chunk) >= 1) |
1114 | 0 | { |
1115 | 0 | *p++ = ' '; |
1116 | 0 | print_hex(*(failedChar - 1)); |
1117 | 0 | } |
1118 | 0 | if ((termCharPtr - failedChar) >= 1) |
1119 | 0 | { |
1120 | 0 | *p++ = ' '; |
1121 | 0 | *p++ = '['; |
1122 | 0 | print_hex(*failedChar); |
1123 | 0 | *p++ = ']'; |
1124 | 0 | } |
1125 | 0 | if ((termCharPtr - failedChar) >= 2) |
1126 | 0 | { |
1127 | 0 | *p++ = ' '; |
1128 | 0 | print_hex(*(failedChar + 1)); |
1129 | 0 | } |
1130 | 0 | if ((termCharPtr - failedChar) >= 3) |
1131 | 0 | { |
1132 | 0 | *p++ = ' '; |
1133 | 0 | print_hex(*(failedChar + 2)); |
1134 | 0 | } |
1135 | 0 | *p++ = ')'; |
1136 | 0 | strm.write(buf, p - buf); |
1137 | 0 | } |
1138 | | |
1139 | 6.11k | return result; |
1140 | 6.11k | } |
1141 | | |
1142 | | bool |
1143 | | MsgHeaderScanner::initialize() |
1144 | 1 | { |
1145 | 1 | initCharInfoArray(); |
1146 | 1 | initStateMachine(); |
1147 | 1 | return true; |
1148 | 1 | } |
1149 | | |
1150 | | |
1151 | | } //namespace resip |
1152 | | |
1153 | | |
1154 | | |
1155 | | #if defined(RESIP_MSG_HEADER_SCANNER_DEBUG) && defined(MSG_SCANNER_STANDALONE) |
1156 | | |
1157 | | extern |
1158 | | int |
1159 | | main(unsigned int numArgs, |
1160 | | const char * * argVector) |
1161 | | { |
1162 | | ::resip::MsgHeaderScanner scanner; |
1163 | | scanner.prepareForMessage(0); |
1164 | | char *text = |
1165 | | "status\r\n" |
1166 | | "bobby: dummy\r\n" |
1167 | | "allow: foo, bar, \"don,\\\"xyz\r\n zy\", buzz\r\n\r\n"; |
1168 | | unsigned int textLength = strlen(text); |
1169 | | char chunk[10000]; |
1170 | | strcpy(chunk, text); |
1171 | | ::resip::MsgHeaderScanner::ScanChunkResult scanChunkResult; |
1172 | | char *unprocessedCharPtr; |
1173 | | scanChunkResult = scanner.scanChunk(chunk, 21, &unprocessedCharPtr); |
1174 | | if (scanChunkResult == ::resip::MsgHeaderScanner::scrNextChunk) |
1175 | | { |
1176 | | printf("Scanning another chunk '."); |
1177 | | ::resip::printText(unprocessedCharPtr, 1); |
1178 | | printf("'\n"); |
1179 | | scanChunkResult = |
1180 | | scanner.scanChunk(unprocessedCharPtr, |
1181 | | (chunk + textLength) - unprocessedCharPtr, |
1182 | | &unprocessedCharPtr); |
1183 | | } |
1184 | | if (scanChunkResult != ::resip::MsgHeaderScanner::scrEnd) |
1185 | | { |
1186 | | printf("Error %d at character %d.\n", |
1187 | | scanChunkResult, |
1188 | | unprocessedCharPtr - chunk); |
1189 | | } |
1190 | | return 0; |
1191 | | } |
1192 | | |
1193 | | #endif //!defined(RESIP_MSG_HEADER_SCANNER_DEBUG) } |
1194 | | |
1195 | | /* ==================================================================== |
1196 | | * The Vovida Software License, Version 1.0 |
1197 | | * |
1198 | | * Copyright (c) 2000-2005 |
1199 | | * |
1200 | | * Redistribution and use in source and binary forms, with or without |
1201 | | * modification, are permitted provided that the following conditions |
1202 | | * are met: |
1203 | | * |
1204 | | * 1. Redistributions of source code must retain the above copyright |
1205 | | * notice, this list of conditions and the following disclaimer. |
1206 | | * |
1207 | | * 2. Redistributions in binary form must reproduce the above copyright |
1208 | | * notice, this list of conditions and the following disclaimer in |
1209 | | * the documentation and/or other materials provided with the |
1210 | | * distribution. |
1211 | | * |
1212 | | * 3. The names "VOCAL", "Vovida Open Communication Application Library", |
1213 | | * and "Vovida Open Communication Application Library (VOCAL)" must |
1214 | | * not be used to endorse or promote products derived from this |
1215 | | * software without prior written permission. For written |
1216 | | * permission, please contact vocal@vovida.org. |
1217 | | * |
1218 | | * 4. Products derived from this software may not be called "VOCAL", nor |
1219 | | * may "VOCAL" appear in their name, without prior written |
1220 | | * permission of Vovida Networks, Inc. |
1221 | | * |
1222 | | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
1223 | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
1224 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
1225 | | * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
1226 | | * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
1227 | | * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
1228 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
1229 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
1230 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
1231 | | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1232 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
1233 | | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
1234 | | * DAMAGE. |
1235 | | * |
1236 | | * ==================================================================== |
1237 | | * |
1238 | | * This software consists of voluntary contributions made by Vovida |
1239 | | * Networks, Inc. and many individuals on behalf of Vovida Networks, |
1240 | | * Inc. For more information on Vovida Networks, Inc., please see |
1241 | | * <http://www.vovida.org/>. |
1242 | | * |
1243 | | */ |