LCOV - code coverage report
Current view: top level - minipot/http - http_minipot_session_process.c (source / functions) Hit Total Coverage
Test: sentinel-minipot-2.3.0 Code Coverage Lines: 0 268 0.0 %
Date: 2022-11-27 17:36:11 Functions: 0 15 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #include "http_minipot_session_process.h"
       2             : #include "http_minipot_session_utils.h"
       3             : #include "http_minipot_session_report.h"
       4             : #include "http_minipot_session_respond.h"
       5             : #include "http_header.gperf.c"
       6             : #include "http_tr_enc.gperf.c"
       7             : #include <base64c.h>
       8             : #include <minipot_utils.h>
       9             : #include "log.h"
      10             : 
      11             : #define FLOW_GUARD_RESP(cmd, s) do { \
      12             :                 if (cmd) { \
      13             :                         http_send_bad_req(s); \
      14             :                         return -1; \
      15             :                 } \
      16             :         } while (0)
      17             : 
      18             : #define BASIC "Basic"
      19             : #define BASIC_LEN strlen(BASIC)
      20             : 
      21           0 : static void proc_auth_data(struct http_minipot_session *s) {
      22           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
      23           0 :         uint8_t sep[] = {'\t', ' '};
      24           0 :         size_t tokens_cnt = minipot_tokenize(s->auth, s->auth_len,
      25           0 :                 s->http_minipot->tokens, TOKENS_LEN, sep, sizeof(sep));
      26           0 :         if (tokens_cnt != 2
      27           0 :                         || BASIC_LEN != s->http_minipot->tokens[0].len
      28           0 :                         || strncmp(BASIC, s->http_minipot->tokens[0].start_ptr, BASIC_LEN)
      29           0 :                         || base64_verify(s->http_minipot->tokens[1].start_ptr,
      30             :                                 s->http_minipot->tokens[1].len))
      31             :                 // wrong scheme or data
      32           0 :                 goto err;
      33           0 :         size_t dcoded_data_len = base64_decode(s->http_minipot->tokens[1].start_ptr,
      34           0 :                 s->http_minipot->tokens[1].len, s->http_minipot->dcode_buff);
      35           0 :         uint8_t *delim = memchr(s->http_minipot->dcode_buff, ':', dcoded_data_len);
      36           0 :         if (!delim)
      37             :                 // wrong format of decoded data
      38           0 :                 goto err;
      39           0 :         char *username = s->http_minipot->dcode_buff;
      40           0 :         size_t username_len = delim - s->http_minipot->dcode_buff;
      41           0 :         char *password = delim + 1;
      42           0 :         size_t password_len = dcoded_data_len - username_len - 1;
      43           0 :         if (http_report_login(s, username, username_len, password, password_len))
      44           0 :                 error("Couldn't report login");
      45           0 :         return;
      46             : 
      47           0 :         err:
      48           0 :         if (http_report_invalid(s))
      49           0 :                 error("Couldn't report invalid");
      50             : }
      51             : 
      52           0 : static int on_mesg_end(struct http_minipot_session *s) {
      53           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
      54           0 :         if (s->auth_len > 0)
      55           0 :                 proc_auth_data(s);
      56             :         else
      57           0 :                 if (http_report_message(s))
      58           0 :                         error("Couldn't report message");
      59           0 :         http_minipot_session_reset_mesg_limits(s);
      60           0 :         MINIPOT_FLOW_GUARD(http_send_unauth(s));
      61           0 :         s->msg_cnt++;
      62           0 :         MINIPOT_FLOW_GUARD(s->msg_cnt == MESSAGES_LIMIT);
      63             :         return 0;
      64             : }
      65             : 
      66           0 : static int proc_chunk(struct http_minipot_session *s, const uint8_t **buffer,
      67             :                 size_t *bytes_to_proc) {
      68           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
      69           0 :         skip_bytes(&s->chunk_size, buffer, bytes_to_proc);
      70           0 :         if (s->chunk_size == 0)
      71           0 :                 s->state = PROC_CHUNK_END;
      72           0 :         return 0;
      73             : }
      74             : 
      75           0 : static int proc_body(struct http_minipot_session *s, const uint8_t **buffer,
      76             :                 size_t *bytes_to_proc) {
      77           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
      78           0 :         skip_bytes(&s->content_len, buffer, bytes_to_proc);
      79           0 :         if (s->content_len == 0)
      80           0 :                 return on_mesg_end(s);
      81             :         return 0;
      82             : }
      83             : 
      84           0 : static int proc_chunk_end(struct http_minipot_session *s) {
      85           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
      86           0 :         size_t token_len = TOKEN_BUFF_LEN - s->token_buff_free_len;
      87             :         // must be empty line
      88           0 :         FLOW_GUARD_RESP(token_len != 0, s);
      89           0 :         s->state = PROC_CHUNK_SIZE;
      90           0 :         return 0;
      91             : }
      92             : 
      93           0 : static int proc_trailer(struct http_minipot_session *s) {
      94           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
      95           0 :         size_t token_len = TOKEN_BUFF_LEN - s->token_buff_free_len;
      96           0 :         if (token_len == 0) {
      97             :                 // end of message
      98           0 :                 return on_mesg_end(s);
      99             :         } else {
     100             :                 // some trailer = header
     101             :                 // here should not be headers we are interested in
     102             :                 // treat them as unknown headers
     103           0 :                 s->header_cnt++;
     104           0 :                 FLOW_GUARD_RESP(s->header_cnt == HEADER_LIMIT, s);
     105           0 :                 uint8_t *double_dot = memchr(s->token_buff, ':', token_len);
     106           0 :                 FLOW_GUARD_RESP(double_dot == NULL, s);
     107           0 :                 uint8_t *header_name_str = s->token_buff;
     108           0 :                 size_t header_name_str_len = double_dot - s->token_buff;
     109           0 :                 uint8_t *header_val_str = double_dot + 1;
     110           0 :                 size_t header_val_str_len = token_len - (double_dot - s->token_buff) - 1;
     111           0 :                 FLOW_GUARD_RESP(
     112             :                         check_header_name(header_name_str, header_name_str_len), s);
     113           0 :                 FLOW_GUARD_RESP(
     114             :                         check_header_val(header_val_str, header_val_str_len), s);
     115             :                 return 0;
     116             :         }
     117             : }
     118             : 
     119           0 : static int proc_chunk_size(struct http_minipot_session *s) {
     120           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     121           0 :         size_t token_len = TOKEN_BUFF_LEN - s->token_buff_free_len;
     122           0 :         FLOW_GUARD_RESP(token_len == 0, s);
     123           0 :         size_t chunk_size_str_len = token_len;
     124           0 :         uint8_t *semicolon = memchr(s->token_buff, ';', token_len);
     125           0 :         if (semicolon) {
     126           0 :                 chunk_size_str_len = semicolon - s->token_buff + 1;
     127           0 :                 FLOW_GUARD_RESP(check_chunk_size_ext(semicolon + 1,
     128             :                         token_len - chunk_size_str_len), s);
     129             :         }
     130             :         // we have to create c-string for strtoll
     131             :         // in this stage it is safe
     132           0 :         s->token_buff[chunk_size_str_len] = '\0';
     133           0 :         char *end_ptr;
     134           0 :         errno = 0;
     135           0 :         int64_t result = strtoll(s->token_buff, &end_ptr, 16);
     136           0 :         FLOW_GUARD_RESP((errno != 0 // conversion error
     137             :                 || end_ptr == (char *)s->token_buff // no digits
     138             :                 || result < 0), // negative value
     139             :                 s);
     140           0 :         s->chunk_size = result;
     141           0 :         if (result == 0)
     142           0 :                 s->state = PROC_TRAILER;
     143             :         else
     144           0 :                 s->state = PROC_CHUNK;
     145             :         return 0;
     146             : }
     147             : 
     148           0 : static int proc_trans_enc_head(struct http_minipot_session *s, uint8_t *val,
     149             :                 size_t len) {
     150           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     151           0 :         s->trans_enc_head_received = true;
     152           0 :         uint8_t sep[] = {'\t', ' ', ','};
     153           0 :         size_t tokens_cnt = minipot_tokenize(val, len, s->http_minipot->tokens,
     154             :                 TOKENS_LEN, sep, sizeof(sep));
     155             :         // check only last encoding - last token
     156           0 :         struct http_transfer_encoding *tr_enc = http_transfer_encoding_lookup(
     157             :                 s->http_minipot->tokens[tokens_cnt - 1].start_ptr,
     158           0 :                 s->http_minipot->tokens[tokens_cnt - 1].len);
     159           0 :         if (tr_enc && tr_enc->transfer_encoding_type == CHUNKED)
     160           0 :                 s->is_chunked_tr_enc_last = true;
     161             :         else
     162           0 :                 s->is_chunked_tr_enc_last = false;
     163             : 
     164           0 :         return 0;
     165             : }
     166             : 
     167           0 : static int proc_con_len_head(struct http_minipot_session *s, uint8_t *val,
     168             :                 size_t len) {
     169           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     170           0 :         if (s->content_len > 0)
     171             :                 // the value has been already assigned - error
     172           0 :                 s->content_len = -1;
     173           0 :         if (s->content_len == 0) {
     174           0 :                 uint8_t sep[] = {'\t', ' '};
     175           0 :                 size_t tokens_cnt = minipot_tokenize(val, len, s->http_minipot->tokens,
     176             :                         TOKENS_LEN, sep, sizeof(sep));
     177           0 :                 FLOW_GUARD_RESP(tokens_cnt != 1, s);
     178             :                 
     179             :                 // we have to create c-string for strtoll
     180             :                 // in this stage it is safe
     181           0 :                 *((uint8_t*)(s->http_minipot->tokens[0].start_ptr)
     182           0 :                         + s->http_minipot->tokens[0].len) = '\0';
     183           0 :                 char *end_ptr;
     184           0 :                 errno = 0;
     185           0 :                 int64_t result = strtoll(s->http_minipot->tokens[0].start_ptr,
     186             :                         &end_ptr, 10);
     187           0 :                 if (errno != 0 // conversion error
     188           0 :                                 || end_ptr == (char *)s->http_minipot->tokens[0].start_ptr // no digits
     189           0 :                                 || result < 0) // negative value
     190           0 :                         s->content_len = -1;
     191             :                 else
     192           0 :                         s->content_len = result;
     193             :         }
     194             :         return 0;
     195             : }
     196             : 
     197           0 : static int proc_user_ag_head(struct http_minipot_session *s, uint8_t *val,
     198             :                 size_t len) {
     199           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     200           0 :         uint8_t *val_start_ptr = val;
     201           0 :         size_t val_len = len;
     202           0 :         size_t prec_ws_len = get_prec_ws_len(val_start_ptr, val_len);
     203           0 :         val_start_ptr += prec_ws_len;
     204           0 :         val_len -= prec_ws_len;
     205           0 :         val_len -= get_trail_ws_len(val_start_ptr, val_len);
     206           0 :         memcpy(s->user_ag, val_start_ptr, val_len);
     207           0 :         s->user_ag_len = val_len;
     208           0 :         return 0;
     209             : }
     210             : 
     211           0 : static int proc_auth_head(struct http_minipot_session *s, uint8_t *val,
     212             :                 size_t len) {
     213           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     214           0 :         uint8_t *val_start_ptr = val;
     215           0 :         size_t val_len = len;
     216           0 :         size_t prec_ws_len = get_prec_ws_len(val_start_ptr, val_len);
     217           0 :         val_start_ptr += prec_ws_len;
     218           0 :         val_len -= prec_ws_len;
     219           0 :         val_len -= get_trail_ws_len(val_start_ptr, val_len);
     220           0 :         memcpy(s->auth, val_start_ptr, val_len);
     221           0 :         s->auth_len = val_len;
     222           0 :         return 0;
     223             : }
     224             : 
     225           0 : static int proc_header(struct http_minipot_session *s) {
     226           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     227           0 :         size_t token_len = TOKEN_BUFF_LEN - s->token_buff_free_len;
     228           0 :         if (token_len == 0) {
     229             :                 // empty line - end of headers
     230           0 :                 if (s->trans_enc_head_received) {
     231           0 :                         if (s->is_chunked_tr_enc_last) {
     232             :                                 // chunked body
     233           0 :                                 s->state = PROC_CHUNK_SIZE;
     234           0 :                                 return 0;
     235             :                         } else {
     236             :                                 // error
     237           0 :                                 http_send_bad_req(s);
     238           0 :                                 return -1;
     239             :                         }
     240             :                 } else {
     241           0 :                         if (s->content_len == -1) {
     242             :                                 // error
     243           0 :                                 http_send_bad_req(s);
     244           0 :                                 return -1;
     245           0 :                         } else if (s->content_len == 0) {
     246             :                                 // no body - end of the message here
     247           0 :                                 return on_mesg_end(s);
     248             :                         } else {
     249             :                                 // normal body
     250           0 :                                 s->state = PROC_BODY;
     251           0 :                                 return 0;
     252             :                         }
     253             :                 }
     254             :         } else {
     255           0 :                 s->header_cnt++;
     256           0 :                 FLOW_GUARD_RESP(s->header_cnt == HEADER_LIMIT, s);
     257             :                 // find header name and value separator
     258           0 :                 uint8_t *double_dot = memchr(s->token_buff, ':', token_len);
     259             :                 // it must be there
     260           0 :                 FLOW_GUARD_RESP(!double_dot, s);
     261             :                 // extract header name
     262           0 :                 uint8_t *header_name_str = s->token_buff;
     263           0 :                 size_t header_name_str_len = double_dot - s->token_buff;
     264             :                 // extract header value
     265           0 :                 uint8_t *header_val_str = double_dot + 1;
     266           0 :                 size_t header_val_str_len = token_len - (double_dot - s->token_buff) - 1;
     267           0 :                 FLOW_GUARD_RESP(
     268             :                         check_header_name(header_name_str, header_name_str_len), s);
     269           0 :                 FLOW_GUARD_RESP(
     270             :                         check_header_val(header_val_str, header_val_str_len), s);
     271           0 :                 struct http_header *header =
     272           0 :                         http_header_name_lookup(header_name_str, header_name_str_len);
     273           0 :                 if (header) {
     274             :                         // known header
     275           0 :                         switch (header->header_type) {
     276           0 :                                 case AUTHORIZATION:
     277           0 :                                         return proc_auth_head(s, header_val_str, header_val_str_len);
     278           0 :                                 case USER_AGENT:
     279           0 :                                         return proc_user_ag_head(s, header_val_str,
     280             :                                                 header_val_str_len);
     281           0 :                                 case CONTENT_LENGTH:
     282           0 :                                         return proc_con_len_head(s, header_val_str,
     283             :                                                 header_val_str_len);
     284           0 :                                 case TRANSFER_ENCODING:
     285           0 :                                         return proc_trans_enc_head(s, header_val_str,
     286             :                                                 header_val_str_len);
     287           0 :                                 default:
     288           0 :                                         error("Invalid header name on session with FD: %d",
     289             :                                                 s->parent.fd);
     290           0 :                                         return -1;
     291             :                         }
     292             :                 }
     293             :                 return 0;
     294             :         }
     295             : }
     296             : 
     297           0 : static int proc_req_line(struct http_minipot_session *s) {
     298           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     299             :         // find first space
     300           0 :         size_t token_len = TOKEN_BUFF_LEN - s->token_buff_free_len;
     301           0 :         uint8_t *first_sp = memchr(s->token_buff, ' ', token_len);
     302             :         // it must be there
     303           0 :         FLOW_GUARD_RESP(!first_sp, s);
     304             :         // find second space
     305           0 :         size_t rest_len = token_len - (first_sp - s->token_buff) - 1;
     306           0 :         uint8_t *second_sp = memchr(first_sp + 1, ' ', rest_len);
     307             :         // it must be there
     308           0 :         FLOW_GUARD_RESP(!second_sp, s);
     309           0 :         FLOW_GUARD_RESP((first_sp + 1) == second_sp, s);
     310           0 :         uint8_t *method_ptr = s->token_buff;
     311           0 :         size_t method_len =  first_sp - s->token_buff;
     312           0 :         uint8_t *url_ptr = first_sp + 1;
     313           0 :         size_t url_len = second_sp - url_ptr;
     314           0 :         uint8_t *version_ptr = second_sp + 1;
     315           0 :         size_t version_len = token_len - (second_sp - s->token_buff) - 1;
     316           0 :         FLOW_GUARD_RESP(check_version(version_ptr, version_len), s);
     317           0 :         FLOW_GUARD_RESP(check_method(method_ptr, method_len), s);
     318           0 :         FLOW_GUARD_RESP(check_url(url_ptr, url_len), s);
     319           0 :         memcpy(s->method, method_ptr, method_len);
     320           0 :         s->method_len = method_len;
     321           0 :         memcpy(s->url, url_ptr, url_len);
     322           0 :         s->url_len = url_len;
     323           0 :         s->state = PROC_HEADER;
     324           0 :         return 0;
     325             : }
     326             : 
     327           0 : static int proc_line(struct http_minipot_session *s, const uint8_t **buffer,
     328             :                 size_t *bytes_to_proc) {
     329           0 :         MINIPOT_TRACE_FUNC_FD(s->parent.fd);
     330             :         // determine data length to be buffered into token buffer
     331           0 :         uint8_t *line_sep = memchr(*buffer, '\n', *bytes_to_proc);
     332           0 :         size_t bytes_to_buff = line_sep ? (line_sep - *buffer + 1) : *bytes_to_proc;
     333           0 :         if (bytes_to_buff <= s->token_buff_free_len) {
     334             :                 // data fits into token buffer
     335             :                 // copy data 
     336           0 :                 memcpy(s->token_buff_wrt_ptr, *buffer, bytes_to_buff);
     337           0 :                 s->token_buff_wrt_ptr += bytes_to_buff;
     338           0 :                 s->token_buff_free_len -= bytes_to_buff;
     339             :                 // shift buffer
     340           0 :                 *buffer += bytes_to_buff;
     341           0 :                 *bytes_to_proc -= bytes_to_buff;
     342           0 :                 if (line_sep) {
     343             :                         // found LF - the line is complete, we can process it
     344             :                         // check for CR, it must be there
     345           0 :                         size_t token_len = TOKEN_BUFF_LEN - s->token_buff_free_len;
     346           0 :                         FLOW_GUARD_RESP(((token_len < 2)
     347             :                                 || (s->token_buff[token_len - 2] != '\r')), s);
     348             :                         // strip CRLF from line buffer
     349           0 :                         s->token_buff_free_len += 2;
     350           0 :                         switch (s->state) {
     351           0 :                                 case PROC_REQ_LINE:
     352           0 :                                         MINIPOT_FLOW_GUARD(proc_req_line(s));
     353             :                                         break;
     354           0 :                                 case PROC_HEADER:
     355           0 :                                         MINIPOT_FLOW_GUARD(proc_header(s));
     356             :                                         break;
     357           0 :                                 case PROC_CHUNK_SIZE:
     358           0 :                                         MINIPOT_FLOW_GUARD(proc_chunk_size(s));
     359             :                                         break;
     360           0 :                                 case PROC_TRAILER:
     361           0 :                                         MINIPOT_FLOW_GUARD(proc_trailer(s));
     362             :                                         break;
     363           0 :                                 case PROC_CHUNK_END:
     364           0 :                                         MINIPOT_FLOW_GUARD(proc_chunk_end(s));
     365             :                                         break;
     366           0 :                                 default:
     367           0 :                                         error("Invalid state on session with FD: %d", s->parent.fd);
     368           0 :                                         return -1;
     369             :                         }
     370           0 :                         http_minipot_session_reset_tk_buff(s);
     371             :                 }
     372           0 :                 return 0;
     373             :         } else {
     374             :                 // data doesn't fit into buffer
     375           0 :                 switch (s->state) {
     376           0 :                         case PROC_REQ_LINE:
     377           0 :                                 http_send_uri_too_long(s);
     378           0 :                                 break;
     379           0 :                         case PROC_HEADER:
     380             :                         case PROC_CHUNK_SIZE:
     381             :                         case PROC_TRAILER:
     382             :                         case PROC_CHUNK_END:
     383           0 :                                 http_send_bad_req(s);
     384           0 :                                 break;
     385           0 :                         default:
     386           0 :                                 error("Invalid state on session with FD: %d", s->parent.fd);
     387           0 :                                 break;
     388             :                 }
     389           0 :                 return -1;
     390             :         }
     391             : }
     392             : 
     393           0 : void proc_rec_data(struct http_minipot_session *s, size_t buff_len) {
     394           0 :         MINIPOT_TRACE_FUNC_P("FD: %d - start", s->parent.fd);
     395           0 :         bool run = true;
     396           0 :         const uint8_t *buff = s->http_minipot->parent.recv_buff;
     397           0 :         while (buff_len > 0 && run) {
     398           0 :                 switch (s->state) {
     399           0 :                         case PROC_REQ_LINE:
     400             :                         case PROC_HEADER:
     401             :                         case PROC_CHUNK_SIZE:
     402             :                         case PROC_TRAILER:
     403             :                         case PROC_CHUNK_END:
     404           0 :                                 if (proc_line(s, &buff, &buff_len)) {
     405           0 :                                         http_minipot_session_close(s);
     406           0 :                                         run = false;
     407             :                                 }
     408             :                                 break;
     409           0 :                         case PROC_BODY:
     410           0 :                                 if (proc_body(s, &buff, &buff_len)) {
     411           0 :                                         http_minipot_session_close(s);
     412           0 :                                         run = false;
     413             :                                 }
     414             :                                 break;
     415           0 :                         case PROC_CHUNK:
     416           0 :                                 if (proc_chunk(s, &buff, &buff_len)) {
     417           0 :                                         http_minipot_session_close(s);
     418           0 :                                         run = false;
     419             :                                 }
     420             :                                 break;
     421           0 :                         default:
     422           0 :                                 error("Invalid state on session with FD: %d", s->parent.fd);
     423           0 :                                 http_minipot_session_close(s);
     424           0 :                                 run = false;
     425           0 :                                 break;
     426             :                 }
     427             :         }
     428           0 :         MINIPOT_TRACE_FUNC_P("- stop", s->parent.fd);
     429           0 : }

Generated by: LCOV version 1.16