"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/preprocessors/HttpInspect/utils/hi_paf.c" (16 Oct 2020, 50850 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "hi_paf.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.16.1_vs_2.9.17.

    1 /* $Id$ */
    2 /****************************************************************************
    3  *
    4  * Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
    5  * Copyright (C) 2011-2013 Sourcefire, Inc.
    6  *
    7  * This program is free software; you can redistribute it and/or modify
    8  * it under the terms of the GNU General Public License Version 2 as
    9  * published by the Free Software Foundation.  You may not use, modify or
   10  * distribute this program under any other version of the GNU General
   11  * Public License.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   21  *
   22  ****************************************************************************/
   23 
   24 //--------------------------------------------------------------------
   25 // hi stuff
   26 //
   27 // @file    hi_paf.c
   28 // @author  Russ Combs <rcombs@sourcefire.com>
   29 
   30 // the goal is to perform the minimal http paf parsing required for
   31 // correctness while maintaining loose coupling with hi proper:
   32 
   33 // * distinguish request from response by presence of http version
   34 //   as first token in first header of response
   35 // * identify head request so response is understood to not have body
   36 // * determine length of body from content-length header
   37 // * determine chunking from transfer-endoding header
   38 // * extract chunk lengths for body chunks
   39 // * determine end of chunks from chunk length of zero
   40 
   41 // 1XX, 204, or 304 status responses must not have a body per RFC but
   42 // if other headers indicate a body is present we will process that.
   43 // This is different for head responses because content-length or
   44 // transfer-encoding are expected.
   45 //
   46 // TBD:
   47 // * Expect: 100-continue
   48 // * Range, Content-Range, and multipart
   49 //--------------------------------------------------------------------
   50 
   51 #ifdef HAVE_CONFIG_H
   52 #include "config.h"
   53 #endif
   54 
   55 #include <assert.h>
   56 #include <ctype.h>
   57 #include <stdio.h>
   58 #include <stdint.h>
   59 #include <stdlib.h>
   60 #include <string.h>
   61 #include <stdbool.h>
   62 
   63 
   64 #include "generators.h"
   65 #include "hi_paf.h"
   66 #include "hi_eo_events.h"
   67 #include "decode.h"
   68 #include "snort.h"
   69 #include "stream_api.h"
   70 #include "snort_debug.h"
   71 #include "snort_httpinspect.h"
   72 
   73 #ifdef DEBUG_MSGS
   74 #define HI_TRACE     // define for state trace
   75 #endif
   76 
   77 #define HIF_REQ 0x0001  // message is request
   78 #define HIF_RSP 0x0002  // message is response
   79 #define HIF_LEN 0x0004  // content-length
   80 #define HIF_CHK 0x0008  // transfer-encoding: chunked
   81 #define HIF_NOB 0x0010  // head (no body in response)
   82 #define HIF_NOF 0x0020  // no flush (chunked body follows)
   83 #define HIF_V09 0x0040  // simple request version 0.9
   84 #define HIF_V10 0x0080  // server response version 1.0
   85 #define HIF_V11 0x0100  // server response version 1.1
   86 #define HIF_ERR 0x0200  // flag error for deferred abort (at eoh)
   87 #define HIF_GET 0x0400  // post (requires content-length or chunks)
   88 #define HIF_PST 0x0800  // post (requires content-length or chunks)
   89 #define HIF_EOL 0x1000  // already saw at least one eol (for v09)
   90 #define HIF_ECD 0x2000  // Content Encoding 
   91 #define HIF_UPG 0x4000  // Http/2 Upgrade:h2c
   92 #define HIF_TST 0x8000  // HTTPv1.0 with chunked header, peek first few bytes of body
   93 #define HIF_END 0x10000  //seen end of header
   94 #define HIF_ALT 0x20000  // alet for no header
   95 #define HIF_CHE 0x40000 //HTTP chunk extension
   96 #define HIF_V1X 0x80000  // invalid HTTP 1.XX version
   97 
   98 enum FlowDepthState
   99 {
  100     HI_FLOW_DEPTH_STATE_NONE = 0,       //start 
  101     HI_FLOW_DEPTH_STATE_CONT_LEN,       //content length start
  102     HI_FLOW_DEPTH_STATE_CHUNK_START,    //chunked encoding start
  103     HI_FLOW_DEPTH_STATE_CHUNK_DISC_SKIP,//chunked encoding discard skip after flow depth
  104     HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT,//chunked encoding discard continue till end of PDU
  105 };
  106 
  107 
  108 typedef struct {
  109     int flow_depth;
  110     uint32_t len;
  111     uint32_t flags;
  112     uint32_t pipe;
  113     uint32_t paf_bytes;
  114     uint8_t msg;
  115     uint8_t fsm;
  116     uint8_t flow_depth_state;
  117     uint8_t char_end_of_header;
  118     uint8_t junk_chars;
  119     bool eoh;
  120     bool disable_chunked;
  121     bool valid_http;
  122     bool fast_blocking;
  123 } Hi5State;
  124 
  125 // config stuff
  126 static uint32_t hi_cap = 0;
  127 
  128 // stats
  129 static uint32_t hi_paf_calls = 0;
  130 static uint32_t hi_paf_bytes = 0;
  131 
  132 //Reset flow depth if required ( for chunked transfer encoding )
  133 static bool flow_depth_reset = false;
  134 
  135 static uint8_t hi_paf_id = 0;
  136 
  137 static bool hi_eoh_found = false;
  138 static bool hi_is_chunked = false;
  139 
  140 #ifdef TARGET_BASED
  141 extern int16_t hi_app_protocol_id;
  142 #endif
  143 
  144 #define MAX_JUNK_CHAR_BEFORE_RESP_STATUS 127
  145 #define  MAX_CHAR_BEFORE_RESP_STATUS 255
  146 #define MAX_CHAR_AFTER_CHUNK_SIZE 255
  147 
  148 
  149 
  150 //--------------------------------------------------------------------
  151 // char classification stuff per RFC 2616
  152 //--------------------------------------------------------------------
  153 
  154 #define CLASS_ERR 0x00
  155 #define CLASS_ANY 0x01
  156 #define CLASS_CHR 0x02
  157 #define CLASS_TOK 0x04
  158 #define CLASS_CRT 0X08
  159 #define CLASS_LWS 0x10
  160 #define CLASS_SEP 0x20
  161 #define CLASS_EOL 0x40
  162 #define CLASS_DIG 0x80
  163 
  164 static const uint8_t class_map[256] =
  165 {
  166 //                                                         tab    lf                cr
  167     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x33, 0x43, 0x03, 0x03, 0x0B, 0x03, 0x03,
  168     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
  169 //
  170 //   ' ',  '!',  '"',  '#',  '$',  '%',  '&',  ''',  '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/'
  171     0x33, 0x07, 0x23, 0x07, 0x07, 0x07, 0x07, 0x07, 0x23, 0x23, 0x07, 0x07, 0x23, 0x07, 0x07, 0x23,
  172 //
  173 //   '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?'
  174     0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
  175 //
  176 //   '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O' 
  177     0x23, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
  178 //
  179 //   'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z',  '[',  '\',  ']',  '^',  '_' 
  180     0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x23, 0x23, 0x23, 0x07, 0x07,
  181 //
  182 //   '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o' 
  183     0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
  184 //
  185 //   'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z',  '{',  '|',  '}',  '~',  '.' 
  186     0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x23, 0x07, 0x23, 0x07, 0x03,
  187 //
  188 //  non-ascii
  189     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  190     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  191     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  192     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  193     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  194     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  195     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  196     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  197 };
  198 
  199 // define class strings with ctl bytes to avoid collision
  200 #define LWSS "\1"  // matches LWS (excludes \n)
  201 #define EOLS "\2"  // matches LF (excludes \r)
  202 #define ANYS "\3"  // matches OCTET; other not used so
  203                    // set other = match for consistency
  204 #define TOKS "\4"  // matches token
  205 #define SEPS "\5"  // matches SEP
  206 #define CHRS "\6"  // matches CHAR
  207 #define DIGS "\7"  // matches DIGIT
  208 #define CRET "\10" // matches CR
  209 
  210 static inline bool is_lwspace(const char c)
  211 {
  212     return (c == ' ' || c == '\t');
  213 }
  214 
  215 static uint8_t Classify (const char* s)
  216 {
  217     if ( !strcmp(s, ANYS) )
  218         return CLASS_ANY;
  219 
  220     if ( !strcmp(s, CHRS) )
  221         return CLASS_CHR;
  222 
  223     if ( !strcmp(s, TOKS) )
  224         return CLASS_TOK;
  225 
  226     if ( !strcmp(s, CRET) )
  227         return CLASS_CRT;
  228 
  229     if ( !strcmp(s, LWSS) )
  230         return CLASS_LWS;
  231 
  232     if ( !strcmp(s, SEPS) )
  233         return CLASS_SEP;
  234 
  235     if ( !strcmp(s, EOLS) )
  236         return CLASS_EOL;
  237 
  238     if ( !strcmp(s, DIGS) )
  239         return CLASS_DIG;
  240 
  241     return CLASS_ERR;
  242 }
  243 
  244 //--------------------------------------------------------------------
  245 // fsm stuff
  246 //--------------------------------------------------------------------
  247 
  248 typedef enum {
  249     ACT_NOP, ACT_NOB,
  250     ACT_GET, ACT_PST,
  251     ACT_V09, ACT_V10, ACT_V11, ACT_V1X,
  252     ACT_SRL, ACT_REQ, ACT_RSP,
  253     ACT_SHI, ACT_SHX,
  254     ACT_LNB, ACT_LNC, ACT_LN0, ACT_ECD,
  255     ACT_CHK, ACT_CK0, ACT_HDR,
  256     ACT_H2, ACT_UPG,
  257     ACT_JNK
  258 } Action;
  259 
  260 typedef struct {
  261     uint8_t action;
  262     uint8_t next;
  263 } Cell;
  264 
  265 typedef struct {
  266     Cell cell[256];
  267 } State;
  268 
  269 static State* hi_fsm = NULL;
  270 static unsigned hi_fsm_size = 0;
  271 
  272 #define EOL '\n'  // \r is ignored
  273 #define LWS ' '   // space or tab
  274 
  275 typedef struct {
  276     uint8_t state;
  277     uint8_t match;
  278     uint8_t other;
  279     uint8_t action;
  280     const char* event;
  281 } HiRule;
  282 
  283 // these are just convenient jump points to the start
  284 // of blocks; the states MUST match array index
  285 // more of these make it easier to insert states
  286 #define P0 (0)
  287 #define P1 (P0+8)
  288 #define P2 (P1+11)
  289 #define P3 (P2+2)
  290 #define P4 (P3+2)
  291 
  292 #define Q0 (P4+3)
  293 #define Q1 (Q0+15)
  294 #define Q2 (Q1+6)
  295 #define Q3 (Q2+9)
  296 
  297 #define R2 (Q3+3)
  298 #define R3 (R2+7)
  299 #define R4 (R3+5)
  300 #define R5 (R4+5)
  301 #define R6 (R5+1)
  302 #define R7 (R6+4)
  303 #define R8 (R7+2)
  304 #define R9 (R8+2)
  305 #define R10 (R9+1)
  306 
  307 #define MAX_STATE        (R10+4)
  308 #define RSP_START_STATE  (P0)
  309 #define REQ_START_STATE  (Q0)
  310 #define REQ_V09_STATE_1  (Q1+3)
  311 #define REQ_V09_STATE_2  (Q2)
  312 #define MSG_CHUNK_STATE  (R7)
  313 #define MSG_EOL_STATE    (R10)
  314 #define RSP_ABORT_STATE  (P4)
  315 #define REQ_ABORT_STATE  (Q3)
  316 
  317 // -------------------------------------------------------------------
  318 // byte-by-byte FSM
  319 // -- eliminates need to accumulate a copy of the current token
  320 // -- no need to compare prefixes when branching (just current byte)
  321 //
  322 // however, we must split tokens at branch points to distinguish
  323 // between non-match events at the start or later in the string.
  324 // also must split whenever the action differs from prior.
  325 //
  326 // it is possible to eliminate this split but it requires a lot
  327 // of additional complexity in the compiler.
  328 //
  329 // see below for other possible enhancements.
  330 // -------------------------------------------------------------------
  331 
  332 static HiRule hi_rule[] =
  333 {
  334     // P* are resPonse specific
  335     // http version starts response (8 chars)
  336     { P0+ 0, P0+ 1, P3   , ACT_SRL, "H"  },
  337     { P0+ 1, P0+ 2, P3   , ACT_NOP, "TTP/1." },
  338     { P0+ 2, P0+ 5, P0+ 3, ACT_V10, "0"  },
  339     { P0+ 3, P0+ 5, P0+ 4, ACT_V11, "1"  },
  340     { P0+ 4, P0+ 5, P0+ 6, ACT_V11, DIGS },
  341     { P0+ 5, P0+ 5, P0+ 6, ACT_V1X, DIGS },
  342     { P0+ 6, P0+ 7, P4   , ACT_NOP, LWSS },
  343     { P0+ 7, P0+ 7, P1   , ACT_NOP, LWSS },
  344 
  345     // now get status code (3 chars)
  346     { P1+ 0, P1+ 6, P1+ 1, ACT_NOB, "1"  },
  347     { P1+ 1, P1+ 3, P1+ 2, ACT_NOP, "2"  },
  348     { P1+ 2, P1+ 3, P1+ 5, ACT_NOP, "3"  },
  349     { P1+ 3, P1+ 4, P1+ 6, ACT_NOP, "0"  },
  350     { P1+ 4, P1+ 8, P1+ 7, ACT_NOB, "4"  },
  351     { P1+ 5, P1+ 6, P1+ 6, ACT_NOP, DIGS },
  352     { P1+ 6, P1+ 7, P1+ 7, ACT_NOP, DIGS },
  353     { P1+ 7, P1+ 8, P4+ 2, ACT_NOP, DIGS },
  354     { P1+ 8, P1+ 10, P1+ 9, ACT_NOP, LWSS }, 
  355     // no status message (http response status code followed by \r\n)    
  356     { P1+ 9, R2+ 0, P4+ 2, ACT_RSP, EOLS },
  357     { P1+ 10, P1+ 10, P2, ACT_NOP, LWSS },
  358 
  359     // now get status message (variable)
  360     { P2+ 0, R2+ 0, P2+ 1, ACT_RSP, EOLS },
  361     { P2+ 1, P2+ 0, P2+ 0, ACT_NOP, ANYS },
  362 
  363     { P3+ 0, P0   , P3+ 1,   ACT_JNK, EOLS },
  364     { P3+ 1, P3+ 0, P3+ 0,   ACT_JNK, ANYS },
  365 
  366     // resync state
  367     { P4+ 0, P0   , P4+ 1, ACT_NOP, LWSS },
  368     { P4+ 1, P0   , P4+ 2, ACT_NOP, EOLS },
  369     { P4+ 2, P4   , P4+ 0, ACT_NOP, ANYS },
  370 
  371     // Q* are reQuest specific
  372     // get method required for 0.9 request
  373     { Q0+ 0, Q0+ 1, Q0+ 3, ACT_SRL, "G"  },
  374     { Q0+ 1, Q0+ 2, Q0+10, ACT_NOP, "ET" },
  375     { Q0+ 2, Q1+ 0, Q0+10, ACT_GET, LWSS },
  376     // head method signals no body in response
  377     { Q0+ 3, Q0+ 4, Q0+ 6, ACT_SRL, "H"  },
  378     { Q0+ 4, Q0+ 5, Q0+10, ACT_NOP, "EAD" },
  379     { Q0+ 5, Q1+ 0, Q0+10, ACT_NOB, LWSS },
  380     // post method must have content-length or chunks
  381     { Q0+ 6, Q0+ 7, Q0+12, ACT_SRL, "P"  },
  382     { Q0+ 7, Q0+ 8, Q0+10, ACT_NOP, "O" },
  383     { Q0+ 8, Q0+ 9, Q0+13, ACT_NOP, "ST" },
  384     { Q0+ 9, Q1+ 0, Q0+13, ACT_PST, LWSS },
  385 
  386     { Q0+ 10, Q0+ 11, Q0+ 13, ACT_NOP, "R"  },
  387     { Q0+ 11, R8+ 1, Q0+ 13, ACT_H2, "I * HTTP/2.0"  }, // R8+0 or R8+1
  388 
  389     // other method
  390     { Q0+ 12, Q0+13, Q3+ 0, ACT_SRL, TOKS },
  391     { Q0+ 13, Q0+13, Q0+14, ACT_NOP, TOKS },
  392     { Q0+ 14, Q1+ 0, Q3+ 2, ACT_NOP, LWSS },
  393 
  394     // this gets required URI / next token
  395     { Q1+ 0, R10+ 0, Q1+ 1, ACT_NOP, EOLS },
  396     { Q1+ 1, Q1+ 0, Q1+ 2, ACT_NOP, LWSS },
  397     { Q1+ 2, Q1+ 3, Q1+ 3, ACT_NOP, ANYS },
  398     { Q1+ 3, R10+ 0, Q1+ 4, ACT_V09, EOLS },
  399     { Q1+ 4, Q2+ 0, Q1+ 5, ACT_NOP, LWSS },
  400     { Q1+ 5, Q1+ 3, Q1+ 3, ACT_NOP, ANYS },
  401 
  402     // this gets version, allowing extra tokens
  403     // if start line doesn't end with version,
  404     // assume 0.9 SimpleRequest (1 line header)
  405     { Q2+ 0, R10+ 0, Q2+ 1, ACT_V09, EOLS },
  406     { Q2+ 1, Q2+ 0, Q2+ 2, ACT_NOP, LWSS },
  407     { Q2+ 2, Q2+ 3, Q2+ 8, ACT_NOP, "H"  },
  408     { Q2+ 3, Q2+ 4, Q2+ 8, ACT_NOP, "TTP/1." },
  409     { Q2+ 4, Q2+ 6, Q2+ 5, ACT_V10, "0"  },
  410     { Q2+ 5, Q2+ 6, Q2+ 8, ACT_V11, "1"  },
  411     { Q2+ 6, R2+ 0, Q2+ 7, ACT_REQ, EOLS },
  412     { Q2+ 7, Q2+ 6, Q2+ 8, ACT_NOP, LWSS },
  413     { Q2+ 8, Q2+ 0, Q2+ 0, ACT_NOP, ANYS },
  414 
  415     // resync state
  416     { Q3+ 0, Q0   , Q3+ 1, ACT_NOP, LWSS },
  417     { Q3+ 1, Q0   , Q3+ 2, ACT_NOP, EOLS },
  418     { Q3+ 2, Q3   , Q3+ 0, ACT_NOP, ANYS },
  419 
  420     // R* are request or response
  421     // for simplicity, we don't restrict headers to allowed
  422     // direction of transfer (eg cookie only from client).
  423     // content-length is optional
  424     { R2+ 0, R2+ 1, R3   , ACT_HDR, "C"  },
  425     { R2+ 1, R2+ 2, R10  , ACT_NOP, "ONTENT-"   },
  426     { R2+ 2, R2+ 4, R2+3 , ACT_NOP, "LENGTH"    },
  427     { R2+ 3, R2+ 4, R10  , ACT_ECD, "ENCODING"  },
  428     { R2+ 4, R2+ 4, R2+5 , ACT_NOP, LWSS },
  429     { R2+ 5, R2+ 6, R10   , ACT_NOP, ":"  },
  430     { R2+ 6, R2+ 6, R6   , ACT_LN0, LWSS }, 
  431     // Upgrade parameter
  432     { R3+ 0, R3+ 1, R4   , ACT_HDR, "U"  },
  433     { R3+ 1, R3+ 2, R10   , ACT_NOP, "PGRADE"  },
  434     { R3+ 2, R3+ 2, R3+ 3, ACT_NOP, LWSS },
  435     { R3+ 3, R3+ 4, R10   , ACT_NOP, ":"  },
  436     { R3+ 4, R3+ 4, R9   , ACT_NOP, LWSS },
  437 
  438     // transfer-encoding required for chunks
  439     { R4+ 0, R4+ 1, R10   , ACT_HDR, "T"  },
  440     { R4+ 1, R4+ 2, R10   , ACT_NOP, "RANSFER-ENCODING"  },
  441     { R4+ 2, R4+ 2, R4+ 3, ACT_NOP, LWSS },
  442     { R4+ 3, R4+ 4, R10   , ACT_NOP, ":"  },
  443     { R4+ 4, R4+ 4, R5   , ACT_NOP, LWSS },
  444 
  445     // only recognized encoding
  446     { R5+ 0, R10   , R10   , ACT_CHK, "CHUNKED" },
  447 
  448     // extract decimal content length
  449     { R6+ 0, R2   , R6+ 1, ACT_LNB, EOLS },
  450     { R6+ 1, R6   , R6+ 2, ACT_SHI, DIGS },
  451     { R6+ 2, R6   , R6+ 3, ACT_SHI, LWSS },
  452     { R6+ 3, R10   , R10   , ACT_SHI, ANYS },
  453 
  454     // extract hex chunk length
  455     { R7+ 0, R8   , R7+ 1, ACT_LNC, EOLS },
  456     { R7+ 1, R7   , R7   , ACT_SHX, ANYS },
  457 
  458     // skip to end of line after chunk data
  459     { R8+ 0, R7   , R8+ 1, ACT_LN0, EOLS },
  460     { R8+ 1, R8   , R8   , ACT_NOP, ANYS },
  461 
  462     { R9+ 0, R10   , R10   , ACT_UPG, "h2c" },
  463 
  464     // skip to end of line
  465     { R10+ 0, R2    , R10+1  , ACT_NOP, EOLS },
  466     { R10+ 1, R10+3 , R10+2  , ACT_NOP, CRET },
  467     { R10+ 2, R10   , R10    , ACT_NOP, ANYS },
  468     { R10+ 3, R2    , R10+2  , ACT_NOP, EOLS },
  469 };
  470 static void hi_update_flow_depth_state(Hi5State *hip, uint32_t* fp, PAF_Status *paf );
  471 static void get_flow_depth(void *ssn, uint64_t flags, Hi5State *hip);
  472 static void get_fast_blocking_status(Hi5State *hip);
  473 
  474 
  475 //--------------------------------------------------------------------
  476 // trace stuff
  477 //--------------------------------------------------------------------
  478 
  479 #ifdef HI_TRACE
  480 static void get_state (int s, char* buf, int max)
  481 {
  482     int nbase;
  483     const char* sbase;
  484 
  485     if ( s >= MAX_STATE ) { sbase = "X"; nbase = MAX_STATE; }
  486 
  487     else if ( s >= R8 ) { sbase = "R8"; nbase = R8; }
  488     else if ( s >= R7 ) { sbase = "R7"; nbase = R7; }
  489     else if ( s >= R6 ) { sbase = "R6"; nbase = R6; }
  490     else if ( s >= R5 ) { sbase = "R5"; nbase = R5; }
  491     else if ( s >= R4 ) { sbase = "R4"; nbase = R4; }
  492     else if ( s >= R3 ) { sbase = "R3"; nbase = R3; }
  493     else if ( s >= R2 ) { sbase = "R2"; nbase = R2; }
  494     //else if ( s >= R1 ) { sbase = "R1"; nbase = R1; }
  495     //else if ( s >= R0 ) { sbase = "R0"; nbase = R0; }
  496 
  497     else if ( s >= Q3 ) { sbase = "Q3"; nbase = Q3; }
  498     else if ( s >= Q2 ) { sbase = "Q2"; nbase = Q2; }
  499     else if ( s >= Q1 ) { sbase = "Q1"; nbase = Q1; }
  500     else if ( s >= Q0 ) { sbase = "Q0"; nbase = Q0; }
  501 
  502     else if ( s >= P4 ) { sbase = "P4"; nbase = P4; }
  503     else if ( s >= P3 ) { sbase = "P3"; nbase = P3; }
  504     else if ( s >= P2 ) { sbase = "P2"; nbase = P2; }
  505     else if ( s >= P1 ) { sbase = "P1"; nbase = P1; }
  506     else                { sbase = "P0"; nbase = P0; }
  507 
  508     snprintf(buf, max, "%s+%d", sbase, (s-nbase));
  509 }
  510 
  511 #if 1
  512 static inline bool dump_it (int c)
  513 {
  514     if ( !c ) return false;
  515     return ( isupper(c) || isdigit(c) || strchr(":-/. \t\n", c) );
  516 }
  517 #endif
  518 
  519 static void hi_fsm_dump ()
  520 {
  521 #if 0
  522     unsigned i;
  523     printf("%s", "s\\e");
  524 
  525     for ( i = 0; i < 256; i++ )
  526     {
  527         if ( dump_it(i) )
  528         {
  529             if ( strchr(" \t\n", i) )
  530                 printf(",%02X", i);
  531             else
  532                 printf(",%c", i);
  533         }
  534     }
  535     printf("\n");
  536 
  537     for ( i = 0; i < hi_fsm_size; i++ )
  538     {
  539         unsigned c;
  540         char buf[16];
  541         get_state(i, buf, sizeof(buf));
  542         printf("%s", buf);
  543 
  544         for ( c = 0; c < 256; c++ )
  545         {
  546             if ( dump_it(c) )
  547             {
  548                 get_state(hi_fsm[i].cell[c].next, buf, sizeof(buf));
  549                 printf(",%s", buf);
  550             }
  551         }
  552         printf("\n");
  553     }
  554 #endif
  555 }
  556 #endif
  557 
  558 //--------------------------------------------------------------------
  559 // fsm build
  560 //--------------------------------------------------------------------
  561 
  562 #define TBD 0xFF
  563 
  564 static void hi_load (State* state, int event, HiRule* rule)
  565 {
  566     //assert(state->cell[event].next == TBD);
  567     state->cell[event].action = rule->action;
  568     state->cell[event].next = rule->match;
  569 }
  570 
  571 static void hi_reload (State* state, int event, int next)
  572 {
  573     state->cell[event].action = ACT_NOP;
  574     state->cell[event].next = next;
  575 }
  576 
  577 static void hi_link (State* state, const char* event, HiRule* rule)
  578 {
  579     bool not_done;
  580 
  581     do
  582     {
  583         int i;
  584         uint8_t class = Classify(event);
  585         not_done = strcmp(event, ANYS) != 0;
  586 
  587         if ( !class )
  588         {
  589             hi_load(state, toupper(*event), rule);
  590             hi_load(state, tolower(*event), rule);
  591         }
  592         else for ( i = 0; i < 256; i++ )
  593         {
  594             if ( (state->cell[i].next == TBD) && (class_map[i] & class) )
  595             {
  596                 hi_load(state, toupper(i), rule);
  597                 hi_load(state, tolower(i), rule);
  598             }
  599         }
  600         rule = hi_rule + rule->other;
  601         event = rule->event;
  602     }
  603     while ( not_done );
  604 }
  605 
  606 static void hi_relink (State* state, const char* event, int next)
  607 {
  608     hi_reload(state, toupper(*event), next);
  609     hi_reload(state, tolower(*event), next);
  610 }
  611 
  612 static void hi_link_check (void)
  613 {
  614     unsigned i, j;
  615 
  616     for ( i = 0; i < hi_fsm_size; i++ )
  617     {
  618         for ( j = 0; j < 256; j++ )
  619             assert(hi_fsm[i].cell[j].next != TBD);
  620 
  621         for ( j = 'A'; j <= 'Z'; j++ )
  622             assert(hi_fsm[i].cell[j].next == hi_fsm[i].cell[tolower(j)].next);
  623     }
  624 }
  625 
  626 static bool hi_fsm_compile (void)
  627 {
  628     unsigned i = 0, j;
  629     unsigned max = sizeof(hi_rule) / sizeof(hi_rule[0]);
  630     unsigned next, extra = 0;
  631 
  632     while ( i < max )
  633     {
  634         // verify that HiRule.state corresponds to array index
  635         // HiRule.state is used solely for this sanity check.
  636         assert(hi_rule[i].state == i);
  637 
  638         if ( strlen(hi_rule[i].event) > 1 )
  639             extra += strlen(hi_rule[i].event) - 1;
  640 
  641         i++;
  642     }
  643     hi_fsm_size = max + extra;
  644     assert(hi_fsm_size < TBD);  // using uint8_t for Cell.next and Hi5State.fsm
  645 
  646     hi_fsm = malloc(hi_fsm_size*sizeof(*hi_fsm));
  647     if ( hi_fsm == NULL )
  648     {
  649         DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "Unable to allocate memory for hi_fsm."););
  650         return false;
  651     }
  652     next = max;
  653 
  654     for ( i = 0; i < hi_fsm_size; i++ )
  655         for ( j = 0; j < 256; j++ )
  656             hi_fsm[i].cell[j].next = TBD;
  657 
  658     for ( i = 0; i < max; i++ )
  659     {
  660         int prev = i, j, n = strlen(hi_rule[i].event);
  661         const char* event = hi_rule[i].event;
  662 
  663         hi_link(hi_fsm+i, event, hi_rule+i);
  664 
  665 #if 0
  666         if ( n > 1 )
  667             printf("Expanding %s at %u\n", hi_rule[i].event, next);
  668 #endif
  669 
  670         for ( j = 1; j < n; j++ )
  671         {
  672             event = hi_rule[i].event + j;
  673             hi_link(hi_fsm+next, event, hi_rule+i);
  674 
  675             event = hi_rule[i].event + j - 1;
  676             hi_relink(hi_fsm+prev, event, next);
  677 
  678             prev = next++;
  679         }
  680     }
  681     hi_link_check();
  682     assert(max + extra == next);
  683     return true;
  684 }
  685 
  686 //--------------------------------------------------------------------
  687 // actions
  688 //--------------------------------------------------------------------
  689 
  690 static inline int dton (int c)
  691 {
  692     return c - '0';
  693 }
  694 
  695 static inline int xton (int c)
  696 {
  697     if ( isdigit(c) )
  698         return c - '0';
  699 
  700     if ( isupper(c) )
  701         return c - 'A' + 10;
  702 
  703     return c - 'a' + 10;
  704 }
  705 
  706 static inline void hi_paf_event_post ()
  707 {
  708     SnortEventqAdd(
  709         GENERATOR_SPP_HTTP_INSPECT_CLIENT,
  710         HI_EO_CLIENT_UNBOUNDED_POST+1, 1, 0, 3,
  711         HI_EO_CLIENT_UNBOUNDED_POST_STR, NULL);
  712 }
  713 
  714 static inline void hi_paf_event_simple ()
  715 {
  716     SnortEventqAdd(
  717         GENERATOR_SPP_HTTP_INSPECT_CLIENT,
  718         HI_EO_CLIENT_SIMPLE_REQUEST+1, 1, 0, 3,
  719         HI_EO_CLIENT_SIMPLE_REQUEST_STR, NULL);
  720 }
  721 
  722 static inline void hi_paf_event_msg_size ()
  723 {
  724     SnortEventqAdd(
  725         GENERATOR_SPP_HTTP_INSPECT,
  726         HI_EO_CLISRV_MSG_SIZE_EXCEPTION+1, 1, 0, 3,
  727         HI_EO_CLISRV_MSG_SIZE_EXCEPTION_STR, NULL);
  728 }
  729 
  730 static inline void hi_paf_invalid_chunked ()
  731 {
  732     SnortEventqAdd(
  733         GENERATOR_SPP_HTTP_INSPECT,
  734         HI_EO_CLISRV_INVALID_CHUNKED_ENCODING+1, 1, 0, 3,
  735         HI_EO_CLISRV_INVALID_CHUNKED_EXCEPTION_STR, NULL);
  736 }
  737 
  738 static inline void hi_paf_event_pipe ()
  739 {
  740     SnortEventqAdd(
  741         GENERATOR_SPP_HTTP_INSPECT_CLIENT,
  742         HI_EO_CLIENT_PIPELINE_MAX+1, 1, 0, 3,
  743         HI_EO_CLIENT_PIPELINE_MAX_STR, NULL);
  744 }
  745 
  746 static inline void hi_paf_response_junk_line ()
  747 {
  748     SnortEventqAdd(
  749         GENERATOR_SPP_HTTP_INSPECT,
  750         HI_EO_SERVER_JUNK_LINE_BEFORE_RESP_HEADER+1, 1, 0, 2,
  751         HI_EO_SERVER_JUNK_LINE_BEFORE_RESP_HEADER_STR, NULL);
  752 }
  753 
  754 static inline void hi_paf_response_invalid_chunksize ()
  755 {
  756     SnortEventqAdd(
  757         GENERATOR_SPP_HTTP_INSPECT,
  758         HI_EO_SERVER_INVALID_CHUNK_SIZE+1, 1, 0, 2,
  759         HI_EO_SERVER_INVALID_CHUNK_SIZE_STR, NULL);
  760 }
  761 
  762 static inline void hi_paf_response_invalid_version ()
  763 {
  764     SnortEventqAdd(
  765         GENERATOR_SPP_HTTP_INSPECT,
  766         HI_EO_SERVER_INVALID_VERSION_RESP_HEADER+1, 1, 0, 2,
  767         HI_EO_SERVER_INVALID_VERSION_RESP_HEADER_STR, NULL);
  768 }
  769 
  770 static inline PAF_Status hi_exec (Hi5State* s, Action a, int c, void* ssn)
  771 {
  772     switch ( a )
  773     {
  774         case ACT_HDR:
  775             break;
  776         case ACT_SRL:
  777             break;
  778         case ACT_NOP:
  779             break;
  780         case ACT_V09:
  781             s->flags |= HIF_REQ|HIF_V09|HIF_ERR;
  782             break;
  783         case ACT_V10:
  784             s->flags |= HIF_V10;
  785             break;
  786         case ACT_V11:
  787             s->flags |= HIF_V11;
  788             break;
  789         case ACT_V1X:
  790             if ( !(s->flags & HIF_V1X) )
  791             {
  792                 s->flags |= HIF_V1X;
  793                 hi_paf_response_invalid_version();
  794             }
  795             break;
  796         case ACT_NOB:
  797             s->flags |= HIF_NOB;
  798             break;
  799         case ACT_GET:
  800             s->flags |= HIF_GET;
  801             break;
  802         case ACT_PST:
  803             s->flags |= HIF_PST;
  804             break;
  805         case ACT_REQ:
  806             s->flags |= HIF_REQ;
  807             break;
  808         case ACT_RSP:
  809             s->flags |= HIF_RSP;
  810             break;
  811         case ACT_SHI:
  812             if ( s->flags & HIF_ERR )
  813                 break;
  814             if ( isdigit(c) && (s->len < 429496728) )
  815             {
  816                 s->len = (10 * s->len) + dton(c);
  817             }
  818             else if (!is_lwspace(c))
  819             {
  820                 hi_paf_event_msg_size();
  821                 s->flags |= HIF_ERR;
  822             }
  823             break;
  824         case ACT_SHX:
  825             if ( s->flags & HIF_ERR )
  826                 break;
  827             if ( isxdigit(c) && !(s->len & 0xF8000000) && !(s->flags & HIF_CHE) )
  828             {
  829                 s->len = (s->len << 4) + xton(c);
  830             }
  831             else if((s->len & 0xF8000000)) 
  832             {
  833                 hi_paf_event_msg_size();
  834                 s->flags |= HIF_ERR;
  835                 return PAF_FLUSH;
  836             }
  837             else
  838             {
  839                 if( !(s->flags & HIF_CHE) )
  840                 {
  841                     /*A correct chunk extension starts with semi-colon*/
  842                     if( c != ';' )
  843                     {
  844                         hi_paf_response_invalid_chunksize();
  845                     }
  846                     else
  847                     {
  848                         s->flags |= HIF_CHE;
  849                         s->junk_chars = 1;
  850                     }
  851                 }
  852                 else if( s->flags & HIF_CHE )
  853                 {
  854                     s->junk_chars++;
  855                     if(s->junk_chars >= MAX_CHAR_AFTER_CHUNK_SIZE )
  856                     {
  857                         s->flags |= HIF_ERR;
  858                         s->junk_chars = 0;
  859                         return PAF_FLUSH;
  860                     }
  861                 }
  862             }
  863             break;
  864         case ACT_LNB:
  865             s->flags |= HIF_LEN;
  866             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
  867                 "%s: lnb=%u\n", __FUNCTION__, s->len);)
  868             break;
  869         case ACT_ECD:
  870            s->flags |=HIF_ECD;
  871            break;
  872         case ACT_LNC:
  873             s->flags |= HIF_LEN;
  874             s->flags &= ~HIF_CHE;
  875             s->junk_chars = 0;
  876             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
  877                 "%s: lnc=%u\n", __FUNCTION__, s->len);)
  878             if ( s->len )
  879                 return PAF_SKIP;
  880             if( s->flags & HIF_NOF )
  881                 flow_depth_reset = true;
  882             s->flags &= ~HIF_NOF;
  883             s->msg = 3;
  884             break;
  885         case ACT_LN0:
  886             s->len = 0;
  887             break;
  888         case ACT_CHK:
  889             s->flags |= HIF_CHK;
  890             hi_is_chunked = true;
  891             break;
  892         case ACT_UPG:
  893             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
  894             "%s: Http/1 Connection upgrade possible to Http/2\n", __FUNCTION__);)
  895             s->flags |= HIF_UPG;
  896             break;
  897         case ACT_H2:
  898             DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
  899             "%s: Http/2 Connection preface received\n", __FUNCTION__);)
  900             stream_api->set_session_http2(ssn);
  901             return PAF_ABORT;
  902         case ACT_CK0:
  903             s->flags |= HIF_NOF;
  904             s->flags &= ~HIF_CHK;
  905             s->fsm = MSG_CHUNK_STATE;
  906             s->len = 0;
  907             break;
  908         case ACT_JNK:
  909             if(++(s->junk_chars) >= MAX_JUNK_CHAR_BEFORE_RESP_STATUS)
  910             {
  911                 s->valid_http = false;
  912                 return PAF_ABORT;
  913             }  
  914             break;
  915     }
  916     return PAF_SEARCH;
  917 }
  918 
  919 //--------------------------------------------------------------------
  920 // pipeline
  921 //--------------------------------------------------------------------
  922 
  923 #define MAX_PIPELINE       24
  924 #define PIPELINE_RUPTURED 255
  925 
  926 static void hi_pipe_push (Hi5State* s_req, void* ssn)
  927 {
  928     uint32_t nreq = s_req->pipe & 0xFF;
  929     uint32_t pipe = s_req->pipe >> 8;
  930 
  931     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
  932         "%s: nreq=%d, pipe=0x%X\n", __FUNCTION__, nreq, pipe);)
  933 
  934     if ( nreq == MAX_PIPELINE )
  935     {
  936         if ( stream_api->is_paf_active(ssn, 0) )
  937             hi_paf_event_pipe();
  938     }
  939     else if ( nreq < MAX_PIPELINE )
  940     {
  941         if ( s_req->flags & HIF_NOB )
  942             pipe |= (0x1 << nreq);
  943     }
  944     if ( nreq == PIPELINE_RUPTURED )
  945         return;
  946 
  947     s_req->pipe = (pipe << 8) | ++nreq;
  948 }
  949 
  950 static void hi_pipe_pop (Hi5State* s_rsp, void* ssn)
  951 {
  952     Hi5State* s_req;
  953     uint32_t nreq, pipe;
  954 
  955     void** pv = stream_api->get_paf_user_data(ssn, 1, hi_paf_id);
  956 
  957     if ( !*pv )
  958         return;
  959 
  960     s_req = *pv;
  961 
  962     nreq = s_req->pipe & 0xFF;
  963     pipe = s_req->pipe >> 8;
  964 
  965     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
  966         "%s: nreq=%d, pipe=0x%X\n", __FUNCTION__, nreq, pipe);)
  967 
  968     if ( nreq == 0 || nreq == PIPELINE_RUPTURED )
  969         return;
  970 
  971     if ( --nreq < MAX_PIPELINE )
  972     {
  973         if ( pipe & 0x1 )
  974             s_rsp->flags |= HIF_NOB;
  975     }
  976     pipe >>= 1;
  977     s_req->pipe = (pipe << 8) | nreq;
  978 }
  979 
  980 //--------------------------------------------------------------------
  981 // control
  982 //--------------------------------------------------------------------
  983 
  984 static inline bool simple_allowed (Hi5State* s)
  985 {
  986     return ( (s->fsm == REQ_V09_STATE_1 || s->fsm == REQ_V09_STATE_2)
  987         && s->flags & HIF_GET );
  988 }
  989 
  990 static inline bool have_pdu (Hi5State* s)
  991 {
  992     return ( s->fsm != REQ_START_STATE && s->fsm != RSP_START_STATE );
  993 }
  994 
  995 static inline bool paf_abort (Hi5State* s)
  996 {
  997     return ( s->fsm == REQ_ABORT_STATE || s->fsm == RSP_ABORT_STATE );
  998 }
  999 
 1000 // this is the 2nd step of stateful scanning, which executes
 1001 // the fsm.
 1002 static PAF_Status hi_scan_fsm (Hi5State* s, int c, void* ssn)
 1003 {
 1004     PAF_Status status;
 1005     State* m = hi_fsm + s->fsm;
 1006     Cell* cell = &m->cell[c];
 1007 
 1008 #ifdef HI_TRACE
 1009     char before[16], after[16];
 1010     uint8_t prev = s->fsm;
 1011 #endif
 1012 
 1013     s->fsm = cell->next;
 1014 
 1015 #ifdef HI_TRACE
 1016     get_state(prev, before, sizeof(before));
 1017     get_state(s->fsm, after, sizeof(after));
 1018     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1019         "%s: %s(%u)[0x%2X, '%c'] -> %d,%s(%u)\n",
 1020         __FUNCTION__, before, prev, c, isgraph(c) ? c : '.',
 1021         cell->action, after, s->fsm);)
 1022 #endif
 1023 
 1024     status = hi_exec(s, cell->action, c, ssn);
 1025 
 1026     return status;
 1027 }
 1028 
 1029 static PAF_Status hi_eoh (Hi5State* s, void* ssn)
 1030 {
 1031     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1032         "%s: flags=0x%X, len=%u\n", __FUNCTION__, s->flags, s->len);)
 1033 
 1034     s->eoh = true;
 1035     s->flags &= ~HIF_ALT;
 1036 
 1037     if ( (s->flags & HIF_REQ) )
 1038         hi_pipe_push(s, ssn);
 1039     else
 1040         hi_pipe_pop(s, ssn);
 1041 
 1042     if( (s->flags & HIF_V10 &&
 1043         s->flags & HIF_CHK))
 1044     {
 1045         hi_paf_invalid_chunked();
 1046         s->flags |= HIF_TST;
 1047     }
 1048     if ( (s->flags & HIF_PST) &&
 1049         !(s->flags & (HIF_CHK|HIF_LEN)) )
 1050     {
 1051         hi_paf_event_post();
 1052         s->flags |= HIF_ERR;
 1053     }
 1054 
 1055     if( s->junk_chars && (s->flags & HIF_RSP))
 1056     {
 1057         hi_paf_response_junk_line();
 1058         s->junk_chars = 0;
 1059     }
 1060 
 1061     if ( (s->flags & HIF_ERR) ||
 1062         ((s->flags & HIF_NOB) && (s->flags & HIF_RSP))
 1063     ) {
 1064         if ( s->flags & HIF_V09 )
 1065             hi_paf_event_simple();
 1066 
 1067         hi_exec(s, ACT_LN0, 0, ssn);
 1068         return PAF_FLUSH;
 1069     }
 1070     if ( s->flags & HIF_CHK )
 1071     {
 1072         if( !(s->flags & HIF_TST) )
 1073         {
 1074             uint32_t fp;
 1075             PAF_Status paf = PAF_SEARCH;
 1076             hi_exec(s, ACT_CK0, 0, ssn);
 1077             hi_update_flow_depth_state(s, &fp, &paf );
 1078             return paf;
 1079         }
 1080          return PAF_SEARCH;
 1081     }
 1082     if ( (s->flags & (HIF_REQ|HIF_LEN)) )
 1083         return PAF_FLUSH;
 1084 
 1085     if ( (s->flags & HIF_V11) && (s->flags & HIF_RSP) )
 1086     {
 1087         hi_exec(s, ACT_LN0, 0, ssn);
 1088         hi_paf_event_msg_size();
 1089         return PAF_FLUSH;
 1090     }
 1091     if ( (s->flags & HIF_V10) && (s->flags & HIF_ECD) && !(s->flags & HIF_LEN))
 1092         return PAF_FLUSH; 
 1093     return PAF_ABORT;
 1094 }
 1095 
 1096 // http messages are scanned statefully, char-by-char, in
 1097 // two steps.  this is the 1st step, which figures out
 1098 // end-of-line (eol) and end-of-headers (eoh) from the byte
 1099 // stream.  also unfolds headers before fsm scanning.  this
 1100 // simplified version ignores \r (in the spirit of send strict,
 1101 // recv tolerant, but it would only take 2 more states to check
 1102 // for \r).  the 2nd step is hi_scan_fsm().
 1103 static inline PAF_Status hi_scan_msg (
 1104     Hi5State* s, int c, uint32_t* fp, void* ssn)
 1105 {
 1106     PAF_Status paf = PAF_SEARCH;
 1107     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1108         "%s[%d]: 0x%2X, '%c'\n", __FUNCTION__, s->msg, c, isgraph(c) ? c : '.');)
 1109 
 1110     if ( c == '\r' )
 1111     {
 1112         if ( hi_is_chunked  && hi_eoh_found  )
 1113         {
 1114             hi_paf_response_invalid_chunksize();
 1115             hi_is_chunked = false;
 1116         }
 1117         hi_eoh_found = false;
 1118 
 1119         if ( s->msg != 1 && s->msg != 5 )
 1120         {
 1121             *fp = 0;
 1122             return paf;
 1123         }
 1124     }
 1125     hi_eoh_found = false;
 1126 
 1127     switch ( s->msg )
 1128     {
 1129     case 0:
 1130         if ( c == '\n' )
 1131         {
 1132             if ( !(s->flags & HIF_EOL) )
 1133             {
 1134                 s->flags |= HIF_EOL;
 1135 
 1136                 if ( simple_allowed(s) )
 1137                 {
 1138                     hi_scan_fsm(s, EOL, ssn);
 1139                     paf = hi_eoh(s, ssn);
 1140                 }
 1141                 else
 1142                     s->msg = 1;
 1143             }
 1144             else if ( s->flags & HIF_NOF )
 1145                 paf = hi_scan_fsm(s, EOL, ssn);
 1146             else
 1147                 s->msg = 1;
 1148         }
 1149         else
 1150             paf = hi_scan_fsm(s, c, ssn);
 1151         break;
 1152 
 1153     case 1:
 1154         if ( c == '\n' )
 1155         {
 1156             hi_scan_fsm(s, EOL, ssn);
 1157 
 1158             if ( have_pdu(s) )
 1159                 paf = hi_eoh(s, ssn);
 1160             s->msg = 0;
 1161         }
 1162         else if (c == ' ' || c == '\t' || c == 0xb || c == 0xc)
 1163         {
 1164             if(s->fsm == MSG_EOL_STATE)
 1165             {
 1166              /* This s->char_end_of_header + 3 to avoid \n\r\n and \n\r\r\n */
 1167                s->char_end_of_header = s->char_end_of_header + 3; 
 1168                hi_scan_fsm(s, c, ssn);
 1169                s->msg = 5;
 1170             }
 1171             else if(!(s->flags & HIF_RSP))
 1172         {
 1173             paf = hi_scan_fsm(s, LWS, ssn);
 1174             s->msg = 0;
 1175         }
 1176             else
 1177             {
 1178                *fp = 0;
 1179                return paf;
 1180             }
 1181         }
 1182         else if ( c == '\r')
 1183         {
 1184             if(s->fsm == MSG_EOL_STATE)
 1185             {
 1186                s->char_end_of_header++;
 1187                hi_scan_fsm(s, c, ssn);
 1188                s->msg = 5;
 1189             }
 1190             else
 1191             {
 1192                *fp = 0;
 1193                return paf;
 1194             }
 1195         }
 1196         else
 1197         {
 1198             paf = hi_scan_fsm(s, EOL, ssn);
 1199 
 1200             if ( paf == PAF_SEARCH )
 1201                 paf = hi_scan_fsm(s, c, ssn);
 1202             s->msg = 0;
 1203         }
 1204         break;
 1205 
 1206     case 3:
 1207         if ( c == '\n' )
 1208             paf = hi_eoh(s, ssn);
 1209         else
 1210         {
 1211             s->msg = 4;
 1212         }
 1213         break;
 1214 
 1215     case 4:
 1216         if ( c == '\n' )
 1217         {
 1218             s->msg = 3;
 1219         }
 1220         break;
 1221     case 5:
 1222         if ( c == '\n' )
 1223         {
 1224             hi_eoh_found = true;
 1225             if ( s->char_end_of_header >= 2)
 1226                s->flags |= HIF_END;
 1227             s->char_end_of_header =0;
 1228             hi_scan_fsm(s, EOL, ssn);
 1229 
 1230             if ( have_pdu(s) )
 1231                 paf = hi_eoh(s, ssn);
 1232             s->msg = 0;
 1233         }
 1234         else if (c == '\r' ||c == '\t' || c == ' ' ||  c == 0xb || c == 0xc)
 1235         {
 1236           s->char_end_of_header++;
 1237           hi_scan_fsm(s, c, ssn);
 1238         }
 1239         else
 1240         {
 1241             s->msg = 0;
 1242             hi_scan_fsm(s, c, ssn);
 1243             s->char_end_of_header =0;
 1244         }
 1245         break;
 1246 
 1247     }
 1248 
 1249     if ( paf_abort(s) )
 1250         paf = PAF_ABORT;
 1251 
 1252     else if ( paf != PAF_SEARCH ) {
 1253         /*
 1254          * If the flush point is set based on content-length,
 1255          * then the payload is eligible for pseudo flush for
 1256          * early detection and file processing.
 1257          */
 1258         *fp = s->len;
 1259         if (paf == PAF_FLUSH && s->fast_blocking &&
 1260             !(stream_api->is_session_decrypted(ssn)) )
 1261         {
 1262             paf = PAF_PSEUDO_FLUSH_SEARCH;
 1263         }
 1264     }
 1265 
 1266     if( paf != PAF_SEARCH && paf != PAF_ABORT )
 1267     {
 1268         hi_update_flow_depth_state(s, fp, &paf );
 1269     }
 1270 
 1271     return paf;
 1272 }
 1273 
 1274 //--------------------------------------------------------------------
 1275 // utility
 1276 //--------------------------------------------------------------------
 1277 
 1278 static void hi_reset (Hi5State* s, uint64_t flags, void *ssn)
 1279 {
 1280     s->len = s->msg = 0;
 1281 
 1282     if ( flags & PKT_FROM_CLIENT )
 1283     {
 1284         s->fsm = REQ_START_STATE;
 1285     }
 1286     else
 1287     {
 1288         s->fsm = RSP_START_STATE;
 1289     }
 1290     s->flags = 0;
 1291     s->junk_chars = 0;
 1292     s->flow_depth_state = HI_FLOW_DEPTH_STATE_NONE;
 1293 
 1294     if( flow_depth_reset )
 1295     {
 1296         get_flow_depth(ssn, flags, s);
 1297         flow_depth_reset = false;
 1298     }
 1299 
 1300     get_fast_blocking_status(s);
 1301 
 1302     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1303         "%s: fsm=%u, flags=0x%X\n", __FUNCTION__, s->fsm, s->flags);)
 1304 }
 1305 
 1306 /*peek into first few bytes of response body to guess if this is chunked.*/ 
 1307 bool  peek_chunk_size(const uint8_t* data, uint32_t len)
 1308 {
 1309     bool chunk_len = false;
 1310     int n = 0;
 1311     while ( n < len )
 1312     {
 1313         char c = data[n];
 1314         if(isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
 1315         {
 1316             chunk_len = true;
 1317             //Too big a chunk, probably not chunk size
 1318             if( n > 8 )
 1319                 return false;
 1320         }
 1321         else if( c == '\r' || c == '\n' || c == ';')
 1322         {
 1323             if( chunk_len )
 1324                 return true;
 1325             else
 1326                 return false;
 1327         }
 1328         else
 1329         {
 1330             //other character,cant be chunk size
 1331             return false;
 1332         }
 1333         n++;
 1334     }
 1335     return true;
 1336 }
 1337 
 1338 //--------------------------------------------------------------------
 1339 // callback for stateful scanning of in-order raw payload
 1340 //--------------------------------------------------------------------
 1341 
 1342 static PAF_Status hi_paf (
 1343     void* ssn, void** pv, const uint8_t* data, uint32_t len,
 1344     uint64_t *flags, uint32_t* fp, uint32_t *fp_eoh)
 1345 {
 1346     Hi5State* hip = *pv;
 1347     PAF_Status paf = PAF_SEARCH;
 1348 
 1349     uint32_t n = 0;
 1350     *fp = 0;
 1351 
 1352 #ifdef TARGET_BASED
 1353     if ( session_api )
 1354     {
 1355         int16_t proto_id = session_api->get_application_protocol_id(ssn);
 1356     
 1357         if ( (proto_id > 0) && (proto_id != hi_app_protocol_id) )
 1358         {
 1359              DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1360                         "%s: its not HTTP, no need to detect\n",
 1361                          __FUNCTION__);)
 1362              return PAF_ABORT;
 1363         }
 1364     }
 1365 #endif
 1366 
 1367     if ( !hip )
 1368     {
 1369         // beware - we allocate here but s5 calls free() directly
 1370         // so no pointers allowed
 1371         hip = calloc(1, sizeof(Hi5State));
 1372 
 1373         if ( !hip )
 1374         {
 1375             *flags |= PKT_H1_ABORT;
 1376             return PAF_ABORT;
 1377         }
 1378         *pv = hip;
 1379 
 1380         flow_depth_reset = true;
 1381 
 1382         hi_reset(hip, *flags, ssn );
 1383     }
 1384 
 1385     hip->eoh = false;
 1386 hip->valid_http = true;
 1387     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1388         "%s: len=%u\n", __FUNCTION__, len);)
 1389 
 1390     if ( hip->flags & HIF_ERR )
 1391     {
 1392         *flags |= PKT_H1_ABORT;
 1393         return PAF_ABORT;
 1394     }
 1395 
 1396     if ( hi_cap && (hi_paf_bytes > hi_cap) )
 1397     {
 1398         *flags |= PKT_H1_ABORT;
 1399         return PAF_ABORT;
 1400     }
 1401 
 1402     hi_update_flow_depth_state(hip, fp, &paf);
 1403 
 1404     if( paf != PAF_SEARCH )
 1405     {
 1406         hi_paf_calls++;
 1407 
 1408         if ( paf == PAF_DISCARD_END )
 1409             hi_reset(hip, *flags, ssn);
 1410 
 1411         hi_paf_bytes += *fp;
 1412         
 1413         if (paf == PAF_ABORT)
 1414             *flags |= PKT_H1_ABORT;
 1415         return paf;
 1416     }
 1417 
 1418 
 1419     while ( n < len )
 1420     {
 1421         if( !(hip->flags & HIF_ERR) && hip->flags & HIF_TST )
 1422         {
 1423             if( n != len )
 1424             {
 1425                 if(peek_chunk_size(data + n, len - n))
 1426                 {
 1427                     hi_exec(hip, ACT_CK0, 0, ssn);
 1428                     hip->flags &= ~HIF_LEN;
 1429                     hi_update_flow_depth_state(hip, fp, &paf );
 1430                 }
 1431                 else
 1432                 {
 1433                      if( hip->flags & HIF_LEN )
 1434                      {
 1435                           paf = PAF_FLUSH;
 1436                           *fp = hip->len;
 1437                           hip->flags &= ~HIF_CHK;
 1438                           hi_update_flow_depth_state(hip, fp, &paf );
 1439                      }
 1440                      else
 1441                      {
 1442                          if ( (hip->flags & HIF_PST))
 1443                              hi_paf_event_post();
 1444                          paf = PAF_ABORT;
 1445                      }
 1446                      hip->disable_chunked = true;
 1447                 }
 1448                 hip->flags &= ~HIF_TST;
 1449             }
 1450         }
 1451 
 1452         // jump ahead to next linefeed when possible
 1453         if ( (hip->msg == 0) && (hip->fsm == MSG_EOL_STATE) )
 1454         {
 1455             uint8_t* lf = memchr(data+n, '\n', len-n);
 1456             if ( !lf )
 1457             {
 1458                 n = len;
 1459                 break;
 1460             }
 1461             n += (lf - (data + n));
 1462         }
 1463         if( paf == PAF_SEARCH)
 1464         {
 1465             paf = hi_scan_msg(hip, data[n++], fp, ssn);
 1466             if( (hip->flags & HIF_RSP) && hip->flags &  HIF_END)
 1467             {
 1468                 SnortEventqAdd(
 1469                 GENERATOR_SPP_HTTP_INSPECT,
 1470                 HI_EO_SERVER_NO_RESP_HEADER_END+1, 1, 0, 2,
 1471                 HI_EO_SERVER_NO_RESP_HEADER_END_STR, NULL);
 1472                 hip->flags |= HIF_ALT;
 1473                 hip->flags &= ~HIF_END;
 1474                 hip->char_end_of_header = 0; 
 1475             }
 1476             if (hip->flags & HIF_ALT)
 1477             {
 1478                 hip->char_end_of_header++;
 1479                 if (hip->char_end_of_header  >= MAX_CHAR_BEFORE_RESP_STATUS )
 1480                 { 
 1481                       paf = PAF_ABORT;
 1482                       hip->flags &= ~HIF_ALT;
 1483                       hip->char_end_of_header = 0;
 1484                  }
 1485              }
 1486         }
 1487         
 1488         if ( hip->flags & HIF_UPG)
 1489         {
 1490             *flags |= PKT_UPGRADE_PROTO;
 1491             //Before a client preface is received server can start
 1492             //sending messages. From next packet h2_paf will take over.
 1493             if (*flags & PKT_FROM_SERVER)
 1494             {
 1495                 stream_api->set_session_http2(ssn);
 1496                 stream_api->set_session_http2_upg(ssn);
 1497             }
 1498         }
 1499 
 1500         if ( paf != PAF_SEARCH )
 1501         {
 1502             if ( hip->flags & HIF_ERR )
 1503             {
 1504                 *fp = len;
 1505                 break;
 1506             }
 1507 
 1508             *fp += n;
 1509             if( hip->eoh && (( hip->flags & HIF_PST ))){
 1510                 *fp_eoh = n;
 1511                 if(*fp <= len )
 1512                     *fp_eoh = 0;
 1513             }
 1514 
 1515             if ( paf != PAF_SKIP && paf != PAF_DISCARD_START )
 1516                 hi_reset(hip, *flags, ssn);
 1517 
 1518             if ( hip->fast_blocking && (paf == PAF_SKIP) &&
 1519                  !(stream_api->is_session_decrypted(ssn)) )
 1520                 paf = PAF_PSEUDO_FLUSH_SKIP;
 1521 
 1522             break;
 1523         }
 1524     }
 1525 
 1526     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1527         "%s: paf=%d, rfp=%u\n", __FUNCTION__, paf, *fp);)
 1528 
 1529     hi_paf_calls++;
 1530     hi_paf_bytes += n;
 1531 
 1532     hip->paf_bytes += n;
 1533     if ( paf == PAF_ABORT)
 1534         *flags |= PKT_H1_ABORT;
 1535 
 1536     return paf;
 1537 }
 1538 
 1539 //--------------------------------------------------------------------
 1540 // public stuff
 1541 //--------------------------------------------------------------------
 1542 
 1543 int hi_paf_register_port (
 1544     struct _SnortConfig *sc, uint16_t port, bool client, bool server, tSfPolicyId pid, bool auto_on)
 1545 {
 1546     if ( !ScPafEnabled() )
 1547         return 0;
 1548 
 1549     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1550         "%s: policy %u, port %u\n", __FUNCTION__, pid, port);)
 1551 
 1552     if ( !stream_api )
 1553         return -1;
 1554 
 1555     if ( client )
 1556         hi_paf_id = stream_api->register_paf_port(sc, pid, port, true, hi_paf, auto_on);
 1557 
 1558     if ( server )
 1559         hi_paf_id = stream_api->register_paf_port(sc, pid, port, false, hi_paf, auto_on);
 1560 
 1561     return 0;
 1562 }
 1563 
 1564 int hi_paf_register_service (
 1565     struct _SnortConfig *sc, uint16_t service, bool client, bool server, tSfPolicyId pid, bool auto_on)
 1566 {
 1567     if ( !ScPafEnabled() )
 1568         return 0;
 1569 
 1570     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1571         "%s: policy %u, service %u\n", __FUNCTION__, pid, service);)
 1572 
 1573     if ( !stream_api )
 1574         return -1;
 1575 
 1576     if ( client )
 1577         hi_paf_id = stream_api->register_paf_service(sc, pid, service, true, hi_paf, auto_on);
 1578 
 1579     if ( server )
 1580         hi_paf_id = stream_api->register_paf_service(sc, pid, service, false, hi_paf, auto_on);
 1581 
 1582     return 0;
 1583 }
 1584 
 1585 //--------------------------------------------------------------------
 1586 
 1587 bool hi_paf_init (uint32_t cap)
 1588 {
 1589     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1590         "%s: cap=%u\n",  __FUNCTION__, cap);)
 1591 
 1592     hi_cap = cap;
 1593 
 1594     if ( !hi_fsm_compile() )
 1595         return false;
 1596 
 1597 #ifdef HI_TRACE
 1598     hi_fsm_dump();
 1599 #endif
 1600 
 1601     return true;
 1602 }
 1603 
 1604 void hi_paf_term (void)
 1605 {
 1606     free(hi_fsm);
 1607     DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF,
 1608         "%s: calls=%u, bytes=%u\n",  __FUNCTION__,
 1609         hi_paf_calls, hi_paf_bytes);)
 1610 
 1611     hi_fsm = NULL;
 1612     hi_fsm_size = 0;
 1613 }
 1614 
 1615 //--------------------------------------------------------------------
 1616 
 1617 bool hi_paf_simple_request (void* ssn)
 1618 {
 1619     if ( ssn )
 1620     {
 1621         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 1, hi_paf_id);
 1622 
 1623         if ( s && *s )
 1624             return ( (*s)->flags & HIF_V09 );
 1625     }
 1626     return false;
 1627 }
 1628 
 1629 static void get_fast_blocking_status(Hi5State *hip)
 1630 {
 1631     hip->fast_blocking = GetHttpFastBlockingStatus();
 1632 }
 1633 
 1634 static void  get_flow_depth(void *ssn, uint64_t flags, Hi5State *hip)
 1635 {
 1636     hip->flow_depth = GetHttpFlowDepth(ssn, flags);
 1637 }
 1638 
 1639 static void hi_update_flow_depth_state(Hi5State *hip, uint32_t* fp, PAF_Status *paf )
 1640 {
 1641     //sanity check
 1642     assert( hip );
 1643 
 1644     if( !hip->flow_depth )
 1645         return;
 1646 
 1647     if ( (hip->flags & HIF_ERR) ||
 1648             ((hip->flags & HIF_NOB) ))
 1649         return;
 1650 
 1651     if( hip->flags & HIF_V09 )
 1652         return;
 1653 
 1654     //Apply flow depth for POST in case of request
 1655     if( (hip->flags & HIF_REQ ) &&
 1656             !(hip->flags & HIF_PST) )
 1657         return;
 1658 
 1659     switch( hip->flow_depth_state )
 1660     {
 1661         case HI_FLOW_DEPTH_STATE_NONE:
 1662             if( hip->flags & HIF_LEN )
 1663             {
 1664                 if( hip->len > 0 )
 1665                 {
 1666                     if( hip->flow_depth == -1 )
 1667                     {
 1668                         *paf = PAF_DISCARD_START;
 1669                         *fp = 0;
 1670                         hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CONT_LEN;
 1671                     }
 1672                     else if( hip->flow_depth > 0 )
 1673                     {
 1674                         if( hip->len > hip->flow_depth )
 1675                         {
 1676                             *paf = PAF_DISCARD_START;
 1677                             *fp = hip->flow_depth;
 1678                             hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CONT_LEN;
 1679                         }
 1680                     }
 1681                 }
 1682             }
 1683             else if( hip->flags & HIF_NOF )
 1684             {
 1685                 if( hip->flow_depth == -1 )
 1686                 {
 1687                     *paf = PAF_DISCARD_START;
 1688                     *fp = 0;
 1689                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT;
 1690                 }
 1691                 else if( hip->flow_depth > 0 )
 1692                 {
 1693                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_START;
 1694                 }
 1695 
 1696             }
 1697             break;
 1698 
 1699         case HI_FLOW_DEPTH_STATE_CONT_LEN:
 1700             *paf = PAF_DISCARD_END;
 1701             if( hip->flow_depth > 0 )
 1702                 *fp = hip->len - hip->flow_depth;
 1703             else
 1704                 *fp = hip->len;
 1705             break;
 1706 
 1707         case HI_FLOW_DEPTH_STATE_CHUNK_START:
 1708             if( ( *paf == PAF_SKIP ) && ( hip->flow_depth <= hip->len ) )
 1709             {
 1710                 *paf = PAF_DISCARD_START;
 1711                 *fp = hip->flow_depth;
 1712                 if( hip->flow_depth == hip->len )
 1713                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT;
 1714                 else
 1715                     hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_SKIP;
 1716                 hip->len -= hip->flow_depth;
 1717             }
 1718             else if( *paf == PAF_SKIP )
 1719             {
 1720                 hip->flow_depth -= hip->len;
 1721             }
 1722             break;
 1723 
 1724         case HI_FLOW_DEPTH_STATE_CHUNK_DISC_SKIP:
 1725             if( hip->len )
 1726             {
 1727                 *paf = PAF_SKIP;
 1728                 *fp = hip->len;
 1729                 hip->len = 0;
 1730             }
 1731             hip->flow_depth_state = HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT;
 1732             break;
 1733 
 1734         case HI_FLOW_DEPTH_STATE_CHUNK_DISC_CONT:
 1735             if( !( hip->flags & HIF_NOF) )
 1736             {
 1737                 *paf = PAF_DISCARD_END;
 1738             }
 1739             break;
 1740 
 1741         default:
 1742             break;
 1743     }
 1744 }
 1745 
 1746 bool hi_paf_resp_eoh(void* ssn)
 1747 {
 1748     if ( ssn )
 1749     {
 1750         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 0, hi_paf_id);
 1751 
 1752         if ( s && *s )
 1753             return ( (*s)->eoh );
 1754     }
 1755     return false;
 1756 }
 1757 
 1758 uint32_t hi_paf_resp_bytes_processed(void* ssn)
 1759 {
 1760     if ( ssn )
 1761     {
 1762         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 0, hi_paf_id);
 1763 
 1764         if ( s && *s )
 1765             return ( (*s)->paf_bytes );
 1766     }
 1767     return 0;
 1768 }
 1769 
 1770 bool hi_paf_valid_http(void* ssn)
 1771 {
 1772     if ( ssn )
 1773     {   
 1774         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, 0, hi_paf_id);
 1775          
 1776         if ( s && *s )
 1777             return ((*s)->valid_http);
 1778     }
 1779     return 1;
 1780 }
 1781 bool hi_paf_disable_te(void* ssn, bool to_server)
 1782 {
 1783     if ( ssn )
 1784     {
 1785         Hi5State** s = (Hi5State **)stream_api->get_paf_user_data(ssn, to_server?1:0, hi_paf_id);
 1786 
 1787         if ( s && *s )
 1788             return ( (*s)->disable_chunked);
 1789     }
 1790     return false;
 1791 }
 1792 
 1793 uint32_t hi_paf_get_size()
 1794 {
 1795     return sizeof(Hi5State);
 1796 }
 1797