1 /* $Id$ */
2 /*
3 ** Copyright (C) 2003 Brian Caswell <bmc@snort.org>
4 ** Copyright (C) 2003 Michael J. Pomraning <mjp@securepipe.com>
5 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
6 ** Copyright (C) 2003-2013 Sourcefire, Inc.
7 **
8 ** This program is free software; you can redistribute it and/or modify
9 ** it under the terms of the GNU General Public License Version 2 as
10 ** published by the Free Software Foundation.  You may not use, modify or
11 ** distribute this program under any other version of the GNU General
12 ** Public License.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 */
23 
24 #include <sys/types.h>
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include "sf_types.h"
31 #include "snort_bounds.h"
32 #include "rules.h"
33 #include "treenodes.h"
34 #include "snort_debug.h"
35 #include "decode.h"
36 #include "plugbase.h"
37 #include "parser.h"
38 #include "plugin_enum.h"
39 #include "util.h"
40 #include "mstring.h"
41 #include "sfhashfcn.h"
42 
43 #ifdef WIN32
44 #define PCRE_DEFINITION
45 #endif
46 
47 #include "sp_pcre.h"
48 
49 #include <pcre.h>
50 
51 #include "snort.h"
52 #include "profiler.h"
53 #ifdef PERF_PROFILING
54 PreprocStats pcrePerfStats;
55 extern PreprocStats ruleOTNEvalPerfStats;
56 #endif
57 
58 #include "sfhashfcn.h"
59 #include "detection_options.h"
60 #include "detection_util.h"
61 
62 /*
63  * we need to specify the vector length for our pcre_exec call.  we only care
64  * about the first vector, which if the match is successful will include the
65  * offset to the end of the full pattern match.  If we decide to store other
66  * matches, make *SURE* that this is a multiple of 3 as pcre requires it.
67  */
68 static int s_pcre_init = 1;
69 
70 void SnortPcreInit(struct _SnortConfig *, char *, OptTreeNode *, int);
71 void SnortPcreParse(struct _SnortConfig *, char *, PcreData *, OptTreeNode *);
72 void SnortPcreDump(PcreData *);
73 int SnortPcre(void *option_data, Packet *p);
74 
PcreFree(void * d)75 void PcreFree(void *d)
76 {
77     PcreData *data = (PcreData *)d;
78 
79     free(data->expression);
80     free(data->re);
81     free(data->pe);
82     free(data);
83 }
84 
PcreHash(void * d)85 uint32_t PcreHash(void *d)
86 {
87     int i,j,k,l,expression_len;
88     uint32_t a,b,c,tmp;
89     PcreData *data = (PcreData *)d;
90 
91     expression_len = strlen(data->expression);
92     a = b = c = 0;
93 
94     for (i=0,j=0;i<expression_len;i+=4)
95     {
96         tmp = 0;
97         k = expression_len - i;
98         if (k > 4)
99             k=4;
100 
101         for (l=0;l<k;l++)
102         {
103             tmp |= *(data->expression + i + l) << l*8;
104         }
105 
106         switch (j)
107         {
108             case 0:
109                 a += tmp;
110                 break;
111             case 1:
112                 b += tmp;
113                 break;
114             case 2:
115                 c += tmp;
116                 break;
117         }
118         j++;
119 
120         if (j == 3)
121         {
122             mix(a,b,c);
123             j=0;
124         }
125     }
126 
127     if (j != 0)
128     {
129         mix(a,b,c);
130     }
131 
132     a += RULE_OPTION_TYPE_PCRE;
133     b += data->options;
134 
135     final(a,b,c);
136 
137     return c;
138 }
139 
PcreCompare(void * l,void * r)140 int PcreCompare(void *l, void *r)
141 {
142     PcreData *left = (PcreData *)l;
143     PcreData *right = (PcreData *)r;
144 
145     if (!left || !right)
146         return DETECTION_OPTION_NOT_EQUAL;
147 
148     if (( strcmp(left->expression, right->expression) == 0) &&
149         ( left->options == right->options))
150     {
151         return DETECTION_OPTION_EQUAL;
152     }
153 
154     return DETECTION_OPTION_NOT_EQUAL;
155 }
156 
PcreDuplicatePcreData(void * src,PcreData * pcre_dup)157 void PcreDuplicatePcreData(void *src, PcreData *pcre_dup)
158 {
159     PcreData *pcre_src = (PcreData *)src;
160 
161     pcre_dup->expression = pcre_src->expression;
162     pcre_dup->options = pcre_src->options;
163     pcre_dup->search_offset = 0;
164     pcre_dup->pe = pcre_src->pe;
165     pcre_dup->re = pcre_src->re;
166 }
167 
PcreAdjustRelativeOffsets(PcreData * pcre,uint32_t search_offset)168 int PcreAdjustRelativeOffsets(PcreData *pcre, uint32_t search_offset)
169 {
170     if ((pcre->options & (SNORT_PCRE_INVERT | SNORT_PCRE_ANCHORED)))
171     {
172         return 0; /* Don't search again */
173     }
174 
175     if (pcre->options & ( SNORT_PCRE_HTTP_BUFS ))
176     {
177         return 0;
178     }
179 
180     /* What's coming in has the absolute offset */
181     pcre->search_offset += search_offset;
182 
183     return 1; /* Continue searcing */
184 }
185 
SetupPcre(void)186 void SetupPcre(void)
187 {
188     RegisterRuleOption("pcre", SnortPcreInit, NULL, OPT_TYPE_DETECTION, NULL);
189 #ifdef PERF_PROFILING
190     RegisterPreprocessorProfile("pcre", &pcrePerfStats, 3, &ruleOTNEvalPerfStats, NULL);
191 #endif
192 }
193 
Ovector_Init(struct _SnortConfig * sc,int unused,void * data)194 static void Ovector_Init(struct _SnortConfig *sc, int unused, void *data)
195 {
196     /* Since SO rules are loaded 1 time at startup, regardless of
197      * configuraton, we won't pcre capture count again, so save the max.  */
198     static int s_ovector_max = 0;
199 
200     /* The pcre_fullinfo() function can be used to find out how many
201      * capturing subpatterns there are in a compiled pattern. The
202      * smallest size for ovector that will allow for n captured
203      * substrings, in addition to the offsets of the substring matched
204      * by the whole pattern, is (n+1)*3.  */
205     sc->pcre_ovector_size += 1;
206     sc->pcre_ovector_size *= 3;
207 
208     if (sc->pcre_ovector_size > s_ovector_max)
209         s_ovector_max = sc->pcre_ovector_size;
210 
211     sc->pcre_ovector = (int *) SnortAlloc(s_ovector_max*sizeof(int));
212 }
213 
214 #if SNORT_RELOAD
Ovector_Reload(struct _SnortConfig * sc,int unused,void * data)215 static void Ovector_Reload(struct _SnortConfig *sc, int unused, void *data)
216 {
217     Ovector_Init(sc, unused, data);
218 }
219 #endif
220 
PcreCapture(struct _SnortConfig * sc,const void * code,const void * extra)221 void PcreCapture(struct _SnortConfig *sc, const void *code, const void *extra)
222 {
223     int tmp_ovector_size = 0;
224 
225     pcre_fullinfo((const pcre *)code, (const pcre_extra *)extra,
226         PCRE_INFO_CAPTURECOUNT, &tmp_ovector_size);
227 
228     if (tmp_ovector_size > sc->pcre_ovector_size)
229         sc->pcre_ovector_size = tmp_ovector_size;
230 
231     if (s_pcre_init)
232     {
233         AddFuncToPostConfigList(sc, Ovector_Init, NULL);
234 #if SNORT_RELOAD
235         AddFuncToReloadList(Ovector_Reload, NULL);
236 #endif
237         s_pcre_init = 0;
238     }
239 
240 }
241 
SnortPcreInit(struct _SnortConfig * sc,char * data,OptTreeNode * otn,int protocol)242 void SnortPcreInit(struct _SnortConfig *sc, char *data, OptTreeNode *otn, int protocol)
243 {
244     PcreData *pcre_data;
245     OptFpList *fpl;
246     void *pcre_dup;
247 
248     /*
249      * allocate the data structure for pcre
250      */
251     pcre_data = (PcreData *) SnortAlloc(sizeof(PcreData));
252 
253     SnortPcreParse(sc, data, pcre_data, otn);
254 
255     otn->pcre_flag = 1;
256 
257     fpl = AddOptFuncToList(SnortPcre, otn);
258     fpl->type = RULE_OPTION_TYPE_PCRE;
259 
260     if (add_detection_option(sc, RULE_OPTION_TYPE_PCRE, (void *)pcre_data, &pcre_dup) == DETECTION_OPTION_EQUAL)
261     {
262 #ifdef DEBUG_RULE_OPTION_TREE
263         LogMessage("Duplicate PCRE:\n%d %s\n%d %s\n\n",
264             pcre_data->options, pcre_data->expression,
265             ((PcreData *)pcre_dup)->options,
266             ((PcreData *)pcre_dup)->expression);
267 #endif
268 
269         if (pcre_data->expression)
270             free(pcre_data->expression);
271         if (pcre_data->pe)
272             free(pcre_data->pe);
273         if (pcre_data->re)
274             free(pcre_data->re);
275 
276         free(pcre_data);
277         pcre_data = pcre_dup;
278     }
279 
280     /*
281      * attach it to the context node so that we can call each instance
282      * individually
283      */
284     fpl->context = (void *) pcre_data;
285 
286     if (pcre_data->options & SNORT_PCRE_RELATIVE)
287         fpl->isRelative = 1;
288 
289     if (otn->ds_list[PLUGIN_PCRE] == NULL)
290         otn->ds_list[PLUGIN_PCRE] = (void *)pcre_data;
291 
292     return;
293 }
294 
ValidatePcreHttpContentModifiers(PcreData * pcre_data)295 static inline void ValidatePcreHttpContentModifiers(PcreData *pcre_data)
296 {
297     if( pcre_data->options & SNORT_PCRE_RELATIVE )
298         FatalError("%s(%d): PCRE unsupported configuration : both relative & uri options specified\n",
299                 file_name, file_line);
300 
301     if( pcre_data->options & SNORT_PCRE_RAWBYTES )
302         FatalError("%s(%d): PCRE unsupported configuration : both rawbytes & uri options specified\n",
303                 file_name, file_line);
304 }
305 
SnortPcreParse(struct _SnortConfig * sc,char * data,PcreData * pcre_data,OptTreeNode * otn)306 void SnortPcreParse(struct _SnortConfig *sc, char *data, PcreData *pcre_data, OptTreeNode *otn)
307 {
308     const char *error;
309     char *re, *free_me;
310     char *opts;
311     char delimit = '/';
312     int erroffset;
313     int compile_flags = 0;
314     unsigned http = 0;
315 
316     if(data == NULL)
317     {
318         FatalError("%s (%d): pcre requires a regular expression\n",
319                    file_name, file_line);
320     }
321 
322     free_me = SnortStrdup(data);
323     re = free_me;
324 
325     /* get rid of starting and ending whitespace */
326     while (isspace((int)re[strlen(re)-1])) re[strlen(re)-1] = '\0';
327     while (isspace((int)*re)) re++;
328 
329     if(*re == '!') {
330         pcre_data->options |= SNORT_PCRE_INVERT;
331         re++;
332         while(isspace((int)*re)) re++;
333     }
334 
335     /* now we wrap the RE in double quotes.  stupid snort parser.... */
336     if(*re != '"') {
337         printf("It isn't \"\n");
338         goto syntax;
339     }
340     re++;
341 
342     if(re[strlen(re)-1] != '"')
343     {
344         printf("It isn't \"\n");
345         goto syntax;
346     }
347 
348     /* remove the last quote from the string */
349     re[strlen(re) - 1] = '\0';
350 
351     /* 'm//' or just '//' */
352 
353     if(*re == 'm')
354     {
355         re++;
356         if(! *re) goto syntax;
357 
358         /* Space as a ending delimiter?  Uh, no. */
359         if(isspace((int)*re)) goto syntax;
360         /* using R would be bad, as it triggers RE */
361         if(*re == 'R') goto syntax;
362 
363         delimit = *re;
364     }
365     else if(*re != delimit)
366         goto syntax;
367 
368     pcre_data->expression = SnortStrdup(re);
369 
370     /* find ending delimiter, trim delimit chars */
371     opts = strrchr(re, delimit);
372     if (opts == NULL)
373         goto syntax;
374 
375     if(!((opts - re) > 1)) /* empty regex(m||) or missing delim not OK */
376         goto syntax;
377 
378     re++;
379     *opts++ = '\0';
380 
381     /* process any /regex/ismxR options */
382     while(*opts != '\0') {
383         switch(*opts) {
384         case 'i':  compile_flags |= PCRE_CASELESS;            break;
385         case 's':  compile_flags |= PCRE_DOTALL;              break;
386         case 'm':  compile_flags |= PCRE_MULTILINE;           break;
387         case 'x':  compile_flags |= PCRE_EXTENDED;            break;
388 
389             /*
390              * these are pcre specific... don't work with perl
391              */
392         case 'A':  compile_flags |= PCRE_ANCHORED;            break;
393         case 'E':  compile_flags |= PCRE_DOLLAR_ENDONLY;      break;
394         case 'G':  compile_flags |= PCRE_UNGREEDY;            break;
395 
396             /*
397              * these are snort specific don't work with pcre or perl
398              */
399         case 'R':  pcre_data->options |= SNORT_PCRE_RELATIVE; break;
400         case 'B':  pcre_data->options |= SNORT_PCRE_RAWBYTES; break;
401         case 'O':  pcre_data->options |= SNORT_OVERRIDE_MATCH_LIMIT; break;
402         case 'U':  pcre_data->options |= SNORT_PCRE_HTTP_URI; http++; break;
403         case 'P':  pcre_data->options |= SNORT_PCRE_HTTP_BODY;  http++; break;
404         case 'H':  pcre_data->options |= SNORT_PCRE_HTTP_HEADER;  http++; break;
405         case 'M':  pcre_data->options |= SNORT_PCRE_HTTP_METHOD;  http++; break;
406         case 'C':  pcre_data->options |= SNORT_PCRE_HTTP_COOKIE;  http++; break;
407         case 'I':  pcre_data->options |= SNORT_PCRE_HTTP_RAW_URI; http++; break;
408         case 'D':  pcre_data->options |= SNORT_PCRE_HTTP_RAW_HEADER; http++; break;
409         case 'K':  pcre_data->options |= SNORT_PCRE_HTTP_RAW_COOKIE; http++; break;
410         case 'S':  pcre_data->options |= SNORT_PCRE_HTTP_STAT_CODE; http++; break;
411         case 'Y':  pcre_data->options |= SNORT_PCRE_HTTP_STAT_MSG; http++; break;
412 
413         default:
414             FatalError("%s (%d): unknown/extra pcre option encountered\n", file_name, file_line);
415         }
416         opts++;
417     }
418 
419     if ( http > 1 )
420         ParseWarning("at most one HTTP buffer may be indicated with pcre");
421 
422     if(pcre_data->options & (SNORT_PCRE_HTTP_BUFS))
423         ValidatePcreHttpContentModifiers(pcre_data);
424 
425     /* now compile the re */
426     DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "pcre: compiling %s\n", re););
427     pcre_data->re = pcre_compile(re, compile_flags, &error, &erroffset, NULL);
428 
429     if(pcre_data->re == NULL)
430     {
431         FatalError("%s(%d) : pcre compile of \"%s\" failed at offset "
432                    "%d : %s\n", file_name, file_line, re, erroffset, error);
433     }
434 
435 
436     /* now study it... */
437     pcre_data->pe = pcre_study(pcre_data->re, 0, &error);
438 
439     if (pcre_data->pe)
440     {
441         if ((ScPcreMatchLimitNewConf(sc) != -1) && !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
442         {
443             if (pcre_data->pe->flags & PCRE_EXTRA_MATCH_LIMIT)
444             {
445                 pcre_data->pe->match_limit = ScPcreMatchLimitNewConf(sc);
446             }
447             else
448             {
449                 pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
450                 pcre_data->pe->match_limit = ScPcreMatchLimitNewConf(sc);
451             }
452         }
453 
454 #ifdef PCRE_EXTRA_MATCH_LIMIT_RECURSION
455         if ((ScPcreMatchLimitRecursionNewConf(sc) != -1) && !(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT))
456         {
457             if (pcre_data->pe->flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION)
458             {
459                 pcre_data->pe->match_limit_recursion = ScPcreMatchLimitRecursionNewConf(sc);
460             }
461             else
462             {
463                 pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
464                 pcre_data->pe->match_limit_recursion = ScPcreMatchLimitRecursionNewConf(sc);
465             }
466         }
467 #endif
468     }
469     else
470     {
471         if (!(pcre_data->options & SNORT_OVERRIDE_MATCH_LIMIT) &&
472              ((ScPcreMatchLimitNewConf(sc) != -1) || (ScPcreMatchLimitRecursionNewConf(sc) != -1)))
473         {
474             pcre_data->pe = (pcre_extra *)SnortAlloc(sizeof(pcre_extra));
475             if (ScPcreMatchLimitNewConf(sc) != -1)
476             {
477                 pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
478                 pcre_data->pe->match_limit = ScPcreMatchLimitNewConf(sc);
479             }
480 
481 #ifdef PCRE_EXTRA_MATCH_LIMIT_RECURSION
482             if (ScPcreMatchLimitRecursionNewConf(sc) != -1)
483             {
484                 pcre_data->pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
485                 pcre_data->pe->match_limit_recursion = ScPcreMatchLimitRecursionNewConf(sc);
486             }
487 #endif
488         }
489     }
490 
491     if(error != NULL)
492     {
493         FatalError("%s(%d) : pcre study failed : %s\n", file_name,
494                    file_line, error);
495     }
496 
497     PcreCapture(sc, pcre_data->re, pcre_data->pe);
498 
499     PcreCheckAnchored(pcre_data);
500 
501     free(free_me);
502 
503     return;
504 
505  syntax:
506     free(free_me);
507 
508     FatalError("%s Line %d => unable to parse pcre regex %s\n",
509                file_name, file_line, data);
510 
511 }
512 
PcreCheckAnchored(PcreData * pcre_data)513 void PcreCheckAnchored(PcreData *pcre_data)
514 {
515     int rc;
516     unsigned long int options = 0;
517 
518     if ((pcre_data == NULL) || (pcre_data->re == NULL) || (pcre_data->pe == NULL))
519         return;
520 
521     rc = pcre_fullinfo(pcre_data->re, pcre_data->pe, PCRE_INFO_OPTIONS, (void *)&options);
522     switch (rc)
523     {
524         /* pcre_fullinfo fails for the following:
525          * PCRE_ERROR_NULL - the argument code was NULL
526          *                   the argument where was NULL
527          * PCRE_ERROR_BADMAGIC - the "magic number" was not found
528          * PCRE_ERROR_BADOPTION - the value of what was invalid
529          * so a failure here means we passed in bad values and we should
530          * probably fatal error */
531 
532         case 0:
533             /* This is the success code */
534             break;
535 
536         case PCRE_ERROR_NULL:
537             FatalError("%s(%d) pcre_fullinfo: code and/or where were NULL.\n",
538                        __FILE__, __LINE__);
539 
540         case PCRE_ERROR_BADMAGIC:
541             FatalError("%s(%d) pcre_fullinfo: compiled code didn't have "
542                        "correct magic.\n", __FILE__, __LINE__);
543 
544         case PCRE_ERROR_BADOPTION:
545             FatalError("%s(%d) pcre_fullinfo: option type is invalid.\n",
546                        __FILE__, __LINE__);
547 
548         default:
549             FatalError("%s(%d) pcre_fullinfo: Unknown error code.\n",
550                        __FILE__, __LINE__);
551     }
552 
553     if ((options & PCRE_ANCHORED) && !(options & PCRE_MULTILINE))
554     {
555         /* This means that this pcre rule option shouldn't be reevaluted
556          * even if any of it's relative children should fail to match.
557          * It is anchored to the cursor set by the previous cursor setting
558          * rule option */
559         pcre_data->options |= SNORT_PCRE_ANCHORED;
560     }
561 }
562 
563 /**
564  * Perform a search of the PCRE data.
565  *
566  * @param pcre_data structure that options and patterns are passed in
567  * @param buf buffer to search
568  * @param len size of buffer
569  * @param start_offset initial offset into the buffer
570  * @param found_offset pointer to an integer so that we know where the search ended
571  *
572  * *found_offset will be set to -1 when the find is unsucessful OR the routine is inverted
573  *
574  * @return 1 when we find the string, 0 when we don't (unless we've been passed a flag to invert)
575  */
pcre_search(const PcreData * pcre_data,const char * buf,int len,int start_offset,int * found_offset)576 static int pcre_search(const PcreData *pcre_data,
577                        const char *buf,
578                        int len,
579                        int start_offset,
580                        int *found_offset)
581 {
582     int matched;
583     int result;
584 
585     if(pcre_data == NULL
586        || buf == NULL
587        || len <= 0
588        || start_offset < 0
589        || start_offset >= len
590        || found_offset == NULL)
591     {
592         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
593                                 "Returning 0 because we didn't have the required parameters!\n"););
594         return 0;
595     }
596 
597     *found_offset = -1;
598 
599     result = pcre_exec(pcre_data->re,  /* result of pcre_compile() */
600                        pcre_data->pe,  /* result of pcre_study()   */
601                        buf,            /* the subject string */
602                        len,            /* the length of the subject string */
603                        start_offset,   /* start at offset 0 in the subject */
604                        0,              /* options(handled at compile time */
605                        snort_conf->pcre_ovector,      /* vector for substring information */
606                        snort_conf->pcre_ovector_size);/* number of elements in the vector */
607 
608     if(result >= 0)
609     {
610         matched = 1;
611         /* From the PCRE man page:
612          * When a match is successful, information about captured substrings is returned in pairs of integers,
613          * starting at the beginning of ovector, and continuing up to two-thirds of its length at the most.
614          * The first element of a pair is set to the offset of the first character in a substring, and the
615          * second is set to the offset of the first character after the end of a substring. The first pair,
616          * ovector[0] and ovector[1], identify the portion of the subject string matched by the entire pattern.
617          * The next pair is used for the first capturing subpattern, and so on. The value returned by
618          * pcre_exec() is the number of pairs that have been set. If there are no capturing subpatterns, the
619          * return value from a successful match is 1, indicating that just the first pair of offsets has been set.
620          *
621          * In Snort's case, the ovector size only allows for the first pair and a single int for scratch space.
622          */
623         *found_offset = snort_conf->pcre_ovector[1];
624         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
625                                 "Setting Doe_ptr and found_offset: %p %d\n",
626                                 doe_ptr, found_offset););
627     }
628     else if(result == PCRE_ERROR_NOMATCH)
629     {
630         matched = 0;
631     }
632     else
633     {
634         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "pcre_exec error : %d \n", result););
635         return 0;
636     }
637 
638     /* invert sense of match */
639     if(pcre_data->options & SNORT_PCRE_INVERT)
640     {
641         matched = !matched;
642     }
643 
644     return matched;
645 }
646 
SnortPcre(void * option_data,Packet * p)647 int SnortPcre(void *option_data, Packet *p)
648 {
649     PcreData *pcre_data = (PcreData *)option_data;
650     int found_offset = -1;  /* where is the ending location of the pattern */
651     const uint8_t *base_ptr, *end_ptr, *start_ptr;
652     int dsize;
653     int length; /* length of the buffer pointed to by base_ptr  */
654     int matched = 0;
655     uint8_t rst_doe_flags = 1;
656     unsigned hb_type;
657     DEBUG_WRAP(char *hexbuf;)
658 
659     PROFILE_VARS;
660     PREPROC_PROFILE_START(pcrePerfStats);
661 
662     //short circuit this for testing pcre performance impact
663     if (ScNoPcre())
664     {
665         PREPROC_PROFILE_END(pcrePerfStats);
666         return DETECTION_OPTION_NO_MATCH;
667     }
668 
669     /* This is the HTTP case */
670     if ( (hb_type = pcre_data->options & SNORT_PCRE_HTTP_BUFS) )
671     {
672         const HttpBuffer* hb = GetHttpBuffer(hb_type);
673 
674         if ( hb )
675         {
676             matched = pcre_search(
677                 pcre_data, (const char*)hb->buf, hb->length, 0, &found_offset);
678 
679             if ( matched )
680             {
681                 /* don't touch doe_ptr on URI contents */
682                 PREPROC_PROFILE_END(pcrePerfStats);
683                 return DETECTION_OPTION_MATCH;
684             }
685         }
686         PREPROC_PROFILE_END(pcrePerfStats);
687         return DETECTION_OPTION_NO_MATCH;
688     }
689     /* end of the HTTP case */
690 
691     if( !(pcre_data->options & SNORT_PCRE_RAWBYTES))
692     {
693         if(Is_DetectFlag(FLAG_ALT_DETECT))
694         {
695             dsize = DetectBuffer.len;
696             start_ptr = DetectBuffer.data;
697             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
698                 "using alternative detect buffer in pcre!\n"););
699         }
700         else if(Is_DetectFlag(FLAG_ALT_DECODE))
701         {
702             dsize = DecodeBuffer.len;
703             start_ptr = DecodeBuffer.data;
704             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
705                 "using alternative decode buffer in pcre!\n"););
706         }
707         else
708         {
709             if(IsLimitedDetect(p))
710                 dsize = p->alt_dsize;
711             else
712                 dsize = p->dsize;
713             start_ptr = p->data;
714         }
715     }
716     else
717     {
718         dsize = p->dsize;
719         start_ptr = p->data;
720     }
721 
722     base_ptr = start_ptr;
723     end_ptr = start_ptr + dsize;
724 
725     /* doe_ptr's would be set by the previous content option */
726     if(pcre_data->options & SNORT_PCRE_RELATIVE && doe_ptr)
727     {
728         if(!inBounds(start_ptr, end_ptr, doe_ptr))
729         {
730             DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
731                                     "pcre bounds check failed on a relative content match\n"););
732             PREPROC_PROFILE_END(pcrePerfStats);
733             return DETECTION_OPTION_NO_MATCH;
734         }
735 
736         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
737                                 "pcre ... checking relative offset\n"););
738         base_ptr = doe_ptr;
739         rst_doe_flags = 0;
740     }
741     else
742     {
743         DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
744                                 "pcre ... checking absolute offset\n"););
745         base_ptr = start_ptr;
746     }
747 
748     length = end_ptr - base_ptr;
749 
750     DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH,
751                             "pcre ... base: %p start: %p end: %p doe: %p length: %d\n",
752                             base_ptr, start_ptr, end_ptr, doe_ptr, length););
753 
754     DEBUG_WRAP(hexbuf = hex(base_ptr, length);
755                DebugMessage(DEBUG_PATTERN_MATCH, "pcre payload: %s\n", hexbuf);
756                free(hexbuf);
757                );
758 
759     matched = pcre_search(pcre_data, (const char *)base_ptr, length, pcre_data->search_offset, &found_offset);
760 
761     /* set the doe_ptr if we have a valid offset */
762     if(found_offset > 0)
763     {
764         UpdateDoePtr(((uint8_t *) base_ptr + found_offset), rst_doe_flags);
765     }
766 
767     if (matched)
768     {
769         PREPROC_PROFILE_END(pcrePerfStats);
770         return DETECTION_OPTION_MATCH;
771     }
772 
773     /* finally return 0 */
774     PREPROC_PROFILE_END(pcrePerfStats);
775     return DETECTION_OPTION_NO_MATCH;
776 }
777