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 : }
|