static INLINE int hi_server_decompress(HI_SESSION *Session, HttpSessionData *sd, const u_char *ptr, const u_char *end, URI_PTR *result) { const u_char *start = ptr; int rawbuf_size = end - ptr; int iRet = HI_SUCCESS; int zRet = HI_FATAL_ERR; int compr_depth, decompr_depth; int compr_bytes_read, decompr_bytes_read; int compr_avail, decompr_avail; int total_bytes_read = 0; int chunk_size = 0; int chunk_read = 0; u_char *compr_buffer; u_char *decompr_buffer; compr_depth = sd->decomp_state->compr_depth; decompr_depth = sd->decomp_state->decompr_depth; compr_bytes_read = sd->decomp_state->compr_bytes_read; decompr_bytes_read = sd->decomp_state->decompr_bytes_read; compr_buffer = sd->decomp_state->compr_buffer; decompr_buffer = sd->decomp_state->decompr_buffer; if(Session->server_conf->unlimited_decompress) { compr_avail = compr_depth; decompr_avail = decompr_depth; } else { compr_avail = compr_depth-compr_bytes_read; decompr_avail = decompr_depth - decompr_bytes_read; } /* Apply the server flow depth * If the server flow depth is set then we need to decompress only upto the * server flow depth */ switch ( Session->server_conf->server_flow_depth) { case -1: decompr_avail=0; break; case 0: break; default: if(sd->resp_state.flow_depth_read < Session->server_conf->server_flow_depth) { if(decompr_avail > (Session->server_conf->server_flow_depth - sd->resp_state.flow_depth_read)) decompr_avail = Session->server_conf->server_flow_depth - sd->resp_state.flow_depth_read; } else { decompr_avail = 0; } break; } if(compr_avail <=0 || decompr_avail <=0 || (!compr_buffer) || (!decompr_buffer)) { ResetGzipState(sd->decomp_state); ResetRespState(&(sd->resp_state)); return iRet; } if(rawbuf_size < compr_avail) { compr_avail = rawbuf_size; } if(!(sd->resp_state.last_pkt_contlen)) { if(CheckChunkEncoding(Session, start, end, NULL, compr_buffer, compr_avail, sd->resp_state.last_chunk_size, &chunk_size, &chunk_read ) == 1) { sd->resp_state.last_chunk_size = chunk_size; compr_avail = chunk_read; zRet = uncompress_gzip(decompr_buffer,decompr_avail,compr_buffer, compr_avail, sd, &total_bytes_read, sd->decomp_state->compress_fmt); } else { /* No Content-Length or Transfer-Encoding : chunked */ if(hi_eo_generate_event(Session, HI_EO_SERVER_NO_CONTLEN)) { hi_eo_server_event_log(Session, HI_EO_SERVER_NO_CONTLEN, NULL, NULL); } memcpy(compr_buffer, ptr, compr_avail); zRet = uncompress_gzip(decompr_buffer,decompr_avail,compr_buffer, compr_avail, sd, &total_bytes_read, sd->decomp_state->compress_fmt); } } else { memcpy(compr_buffer, ptr, compr_avail); zRet = uncompress_gzip(decompr_buffer,decompr_avail,compr_buffer, compr_avail, sd, &total_bytes_read, sd->decomp_state->compress_fmt); } sd->decomp_state->compr_bytes_read += compr_avail; hi_stats.compr_bytes_read += compr_avail; if((zRet == HI_SUCCESS) || (zRet == HI_NONFATAL_ERR)) { if(decompr_buffer) { result->uri = decompr_buffer; if ( total_bytes_read < decompr_avail ) { result->uri_end = decompr_buffer + total_bytes_read; sd->decomp_state->decompr_bytes_read += total_bytes_read; sd->resp_state.flow_depth_read += total_bytes_read; hi_stats.decompr_bytes_read += total_bytes_read; } else { result->uri_end = decompr_buffer + decompr_avail; sd->decomp_state->decompr_bytes_read += decompr_avail; sd->resp_state.flow_depth_read += decompr_avail; hi_stats.decompr_bytes_read += decompr_avail; } } } else { ResetGzipState(sd->decomp_state); ResetRespState(&(sd->resp_state)); } return iRet; }
/** ** Find the URI and determine whether the URI needs to be normalized. ** ** This is a big step in stateless inspection, because we need to reliably ** find the URI and when possible filter out non-URIs. We do this using a ** simple state machine that is based on characters found in the data ** buffer. ** ** Another important aspect of the stateless inspection is the ability to ** track and inspect pipelined requests. It is VERY IMPORTANT to reset the ** pipeline_req pointer, since we don't memset the whole structure. This ** pointer is reset in the hi_si_session_inspection() function. Check there ** for more details. ** ** Normalization is detected when we are looking at the packet for the URI. ** We look for the following issues: ** - //// ** - /../ ** - /./ ** - non-ascii charss ** - % ** - \ ** When these things are seen we point to the first occurence in the URI, or ** where we have to start normalizing. If the URI is updated to a new ** pointer, then the normalization pointer is reset and we start over. ** Using this method should cut down the memcpy()s per URI, since most ** URIs are not normalized. ** ** If this function returns HI_NONFATAL_ERR, we return out of mode_inspection ** with an error and abort HttpInspect processing, and continue on with ** any other processing we do. The Session parameters that we use here are ** reset in the next time that we do session_inspection, so we don't do ** any initialization here. ** ** @param Session pointer to the HTTP session ** @param data pointer to the start of the packet payload ** @param dsize size of the payload ** ** @return integer ** ** @retval HI_INVALID_ARG invalid argument ** @retval HI_NONFATAL_ERR no URI detected ** @retval HI_SUCCESS URI detected and Session pointers updated */ static int StatelessInspection(HI_SESSION *Session, unsigned char *data, int dsize) { HTTPINSPECT_CONF *ServerConf; HTTPINSPECT_CONF *ClientConf; HI_CLIENT *Client; URI_PTR uri_ptr; u_char *start; u_char *end; u_char *ptr; int iRet; if(!Session || !data || dsize < 1) { return HI_INVALID_ARG; } ServerConf = Session->server_conf; if(!ServerConf) { return HI_INVALID_ARG; } ClientConf = Session->client_conf; if(!ClientConf) { return HI_INVALID_ARG; } Client = &Session->client; memset(&uri_ptr, 0x00, sizeof(URI_PTR)); /* ** We set the starting boundary depending on whether this request is ** a normal request or a pipeline request. The end boundary is always ** the same whether it is a pipeline request or other. */ if(Client->request.pipeline_req) { start = Client->request.pipeline_req; } else { start = data; } end = data + dsize; ptr = start; /* ** Apache and IIS strike again . . . Thanks Kanatoko ** - Ignore CRLFs at the beginning of the request. */ while(hi_util_in_bounds(start, end, ptr)) { if(*ptr < 0x21) { if(*ptr < 0x0E && *ptr > 0x08) { ptr++; continue; } else { if(*ptr == 0x20) { ptr++; continue; } } } break; } uri_ptr.uri = ptr; uri_ptr.uri_end = end; /* ** This loop compares each char to an array of functions ** (one for each char) and calling that function if there is one. ** ** If there is no function, then we just increment the char ptr and ** continue processing. ** ** If there is a function, we call that function and process. It's ** important to note that the function that is called is responsible ** for incrementing the ptr to the next char to be inspected. The ** loop does not increment the pointer when a function is called to ** allow the maximum flexibility to the functions. */ while(hi_util_in_bounds(start, end, ptr)) { if(lookup_table[*ptr]) { if((iRet = (lookup_table[*ptr])(Session, start, end, &ptr, &uri_ptr))) { if(iRet == URI_END) { /* ** You found a URI, let's break and check it out. */ break; } else if(iRet == HI_OUT_OF_BOUNDS) { /* ** Means you've reached the end of the buffer. THIS ** DOESN'T MEAN YOU HAVEN'T FOUND A URI. */ break; } else /* NO_URI */ { /* ** Check for chunk encoding, because the delimiter can ** also be a space, which would look like a pipeline request ** to us if we don't do this first. */ if(Session->server_conf->chunk_length) CheckChunkEncoding(Session, start, end); /* ** We only inspect the packet for another pipeline ** request if there wasn't a previous pipeline request. ** The reason that we do this is because */ if(!Client->request.pipeline_req) { /* ** Just because there was no URI in the first part ** the packet, doesn't mean that this isn't a ** pipelined request that has been segmented. */ if(!ServerConf->no_pipeline) { if((Client->request.pipeline_req = FindPipelineReq(ptr, end))) { return HI_SUCCESS; } } } return HI_NONFATAL_ERR; } } else { /* ** This means that we found the next non-whitespace char ** and since we are already pointed there, so we just ** continue. */ continue; } } ptr++; } /* ** If there is a pipelined request in this packet, we should always ** see the first space followed by text (which is the URI). Without ** that first space, then we never get to the URI, so we should just ** return, since there is nothing else to inspect. */ if(Client->request.pipeline_req) { if(uri_ptr.uri != uri_ptr.first_sp_end) { if(Session->server_conf->chunk_length) CheckChunkEncoding(Session, start, end); return HI_NONFATAL_ERR; } } /* ** We set the HI_CLIENT variables from the URI_PTR structure. We also ** do error checking for the values in this routine as well. */ if((iRet = SetClientVars(Client, &uri_ptr, dsize))) { return iRet; } /* ** One last check for an oversize directory. This gets the long ** directory when there is a beginning slash and no other slashes ** until the end of the packet. ** ** We do this check after we set the variables, just in case there ** was some errors while setting the variables. This could save some ** false positives on a bad URI setting. */ CheckLongDir(Session, &uri_ptr, ptr); /* ** Check for absolute URI and alert for proxy comm if necessary ** ** NOTE: ** Also check ClientConf for proxy configuration so we don't ** alert on outbound requests from legitimate proxies. */ if(uri_ptr.proxy && Session->global_conf->proxy_alert && (!ServerConf->allow_proxy && !ClientConf->allow_proxy)) { if(hi_eo_generate_event(Session, HI_EO_CLIENT_PROXY_USE)) { hi_eo_client_event_log(Session, HI_EO_CLIENT_PROXY_USE, NULL, NULL); } } /* ** Find the next pipeline request, if one is there. If we don't find ** a pipeline request, then we return NULL here, so this is always ** set to the correct value. */ if(!ServerConf->no_pipeline) { Client->request.pipeline_req = FindPipelineReq(uri_ptr.delimiter, end); } else { Client->request.pipeline_req = NULL; } if(Session->server_conf->chunk_length) CheckChunkEncoding(Session, uri_ptr.delimiter, end); return HI_SUCCESS; }
static INLINE int hi_server_extract_body( HI_SESSION *Session, HttpSessionData *sd, const u_char *ptr, const u_char *end, URI_PTR *result) { HTTPINSPECT_CONF *ServerConf; const u_char *start = ptr; int iRet = HI_SUCCESS; const u_char *post_end = end; int chunk_size = 0; int chunk_read = 0; int bytes_to_read = 0; ServerConf = Session->server_conf; switch(ServerConf->server_flow_depth) { case -1: result->uri = result->uri_end = NULL; return iRet; case 0: break; default: if(sd->resp_state.flow_depth_read < ServerConf->server_flow_depth) { bytes_to_read = ServerConf->server_flow_depth - sd->resp_state.flow_depth_read; if((end-ptr) > bytes_to_read ) { end = ptr + bytes_to_read; } sd->resp_state.flow_depth_read +=bytes_to_read; } else { result->uri = result->uri_end = NULL; return iRet; } } /* if( ServerConf->server_flow_depth && ((end - ptr) > ServerConf->server_flow_depth) ) { end = ptr + ServerConf->server_flow_depth; }*/ if (!(sd->resp_state.last_pkt_contlen)) { if( ServerConf->chunk_length ) { if(CheckChunkEncoding(Session, start, end, &post_end, (u_char *)DecodeBuffer.data, sizeof(DecodeBuffer.data), sd->resp_state.last_chunk_size, &chunk_size, &chunk_read ) == 1) { sd->resp_state.last_chunk_size = chunk_size; sd->resp_state.last_pkt_chunked = 1; result->uri = (u_char *)DecodeBuffer.data; result->uri_end = result->uri + chunk_read; return iRet; } else { if(!(sd->resp_state.last_pkt_chunked)) { if(hi_eo_generate_event(Session, HI_EO_SERVER_NO_CONTLEN)) { hi_eo_server_event_log(Session, HI_EO_SERVER_NO_CONTLEN, NULL, NULL); } } else { sd->resp_state.last_pkt_chunked = 0; sd->resp_state.last_chunk_size = 0; } result->uri = start; result->uri_end = end; } } else { result->uri = start; result->uri_end = end; return iRet; } } result->uri = start; result->uri_end = end; return STAT_END; }