UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
httpparser.cpp
Go to the documentation of this file.
1/* *****************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (c) 2012 France Telecom All rights reserved.
6 * Copyright (C) 2021+ GPL 3 and higher by Ingo Höft, <Ingo@Hoeft-online.de>
7 * Redistribution only with this Copyright remark. Last modified: 2025-05-29
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * - Neither name of Intel Corporation nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * ****************************************************************************/
34// Last verify with this project pupnp source file on 2023-07-20, ver 1.14.17
40
41#ifndef _GNU_SOURCE
42#define _GNU_SOURCE /* For strcasestr() in string.h */
43#endif
45
46#include <httpparser.hpp>
47#include <statcodes.hpp>
48#include <upnpdebug.hpp>
49
50#include <UPnPsdk/synclog.hpp>
51
53#include <cassert>
54#include <cstdarg>
55#include <limits.h>
57
58/* entity positions */
59
60
61namespace { // anonymous namespace to keep scope local to file
62
65 TT_IDENTIFIER,
66 TT_WHITESPACE,
67 TT_CRLF,
68 TT_CTRL,
69 TT_SEPARATOR,
70 TT_QUOTEDSTRING
71};
72
74// This is prepared for binary search and must be sorted by method-name.
75inline constexpr std::array<const UPnPsdk::str_int_entry, 11> Http_Method_Table{
76 {{"DELETE", HTTPMETHOD_DELETE},
77 {"GET", HTTPMETHOD_GET},
78 {"HEAD", HTTPMETHOD_HEAD},
79 {"M-POST", HTTPMETHOD_MPOST},
80 {"M-SEARCH", HTTPMETHOD_MSEARCH},
81 {"NOTIFY", HTTPMETHOD_NOTIFY},
82 {"POST", HTTPMETHOD_POST},
83 {"SUBSCRIBE", HTTPMETHOD_SUBSCRIBE},
84 {"UNSUBSCRIBE", HTTPMETHOD_UNSUBSCRIBE},
85 {"POST", SOAPMETHOD_POST},
86 {"PUT", HTTPMETHOD_PUT}}};
87
88/* *********************************************************************/
89/* *********** scanner *************/
90/* *********************************************************************/
91
93constexpr char TOKCHAR_CR{0xD};
94constexpr char TOKCHAR_LF{0xA};
96
100inline void scanner_init( //
101 scanner_t* scanner,
102 membuffer* bufptr
103) {
104 scanner->cursor = (size_t)0;
105 scanner->msg = bufptr;
106 scanner->entire_msg_loaded = 0;
107}
108
113 int c
114) {
115 return strchr(" \t()<>@,;:\\\"/[]?={}", c) != 0;
116}
117
121inline int is_identifier_char( //
122 int c
123) {
124 return c >= 32 && c <= 126 && !is_separator_char(c);
125}
126
130inline int is_control_char( //
131 int c
132) {
133 return (c >= 0 && c <= 31) || c == 127;
134}
135
139inline int is_qdtext_char( //
140 int c
141) {
142 /* we don't check for this; it's checked in get_token() */
143 assert(c != '"');
144
145 return (c >= 32 && c != 127) || c < 0 || c == TOKCHAR_CR ||
146 c == TOKCHAR_LF || c == '\t';
147}
148
161 scanner_t* scanner,
162 memptr* token,
163 token_type_t* tok_type
164) {
165 char* cursor;
166 char* null_terminator; /* point to null-terminator in buffer */
167 int c;
168 token_type_t token_type;
169 int got_end_quote;
170
171 assert(scanner);
172 assert(token);
173 assert(tok_type);
174
175 /* point to next char in buffer */
176 cursor = scanner->msg->buf + scanner->cursor;
177 null_terminator = scanner->msg->buf + scanner->msg->length;
178 /* not enough chars in input to parse */
179 if (cursor == null_terminator)
180 return PARSE_INCOMPLETE;
181 c = *cursor;
182 if (is_identifier_char(c)) {
183 /* scan identifier */
184 token->buf = cursor++;
185 token_type = TT_IDENTIFIER;
186 while (cursor < null_terminator && is_identifier_char(*cursor))
187 cursor++;
188 if (!scanner->entire_msg_loaded && cursor == null_terminator)
189 /* possibly more valid chars */
190 return PARSE_INCOMPLETE;
191 /* calc token length */
192 token->length = (size_t)cursor - (size_t)token->buf;
193 } else if (c == ' ' || c == '\t') {
194 token->buf = cursor++;
195 token_type = TT_WHITESPACE;
196 while (cursor < null_terminator && (*cursor == ' ' || *cursor == '\t'))
197 cursor++;
198 if (!scanner->entire_msg_loaded && cursor == null_terminator)
199 /* possibly more chars */
200 return PARSE_INCOMPLETE;
201 token->length = (size_t)cursor - (size_t)token->buf;
202 } else if (c == TOKCHAR_CR) {
203 /* scan CRLF */
204 token->buf = cursor++;
205 if (cursor == null_terminator)
206 /* not enuf info to determine CRLF */
207 return PARSE_INCOMPLETE;
208 if (*cursor != TOKCHAR_LF) {
209 /* couldn't match CRLF; match as CR */
210 token_type = TT_CTRL; /* ctrl char */
211 token->length = (size_t)1;
212 } else {
213 /* got CRLF */
214 token->length = (size_t)2;
215 token_type = TT_CRLF;
216 cursor++;
217 }
218 } else if (c == TOKCHAR_LF) { /* accept \n as CRLF */
219 token->buf = cursor++;
220 token->length = (size_t)1;
221 token_type = TT_CRLF;
222 } else if (c == '"') {
223 /* quoted text */
224 token->buf = cursor++;
225 token_type = TT_QUOTEDSTRING;
226 got_end_quote = 0;
227 while (cursor < null_terminator) {
228 c = *cursor++;
229 if (c == '"') {
230 got_end_quote = 1;
231 break;
232 } else if (c == '\\') {
233 if (cursor < null_terminator) {
234 /* c = *cursor++; */
235 cursor++;
236 /* the char after '\\' could be ANY
237 * octet */
238 }
239 /* else, while loop handles incomplete buf */
240 } else if (is_qdtext_char(c)) {
241 /* just accept char */
242 } else
243 /* bad quoted text */
244 return PARSE_FAILURE;
245 }
246 if (got_end_quote)
247 token->length = (size_t)cursor - (size_t)token->buf;
248 else { /* incomplete */
249
250 assert(cursor == null_terminator);
251 return PARSE_INCOMPLETE;
252 }
253 } else if (is_separator_char(c)) {
254 /* scan separator */
255 token->buf = cursor++;
256 token_type = TT_SEPARATOR;
257 token->length = (size_t)1;
258 } else if (is_control_char(c)) {
259 /* scan ctrl char */
260 token->buf = cursor++;
261 token_type = TT_CTRL;
262 token->length = (size_t)1;
263 } else
264 return PARSE_FAILURE;
265
266 scanner->cursor += token->length; /* move to next token */
267 *tok_type = token_type;
268 return PARSE_OK;
269}
270
277inline char* scanner_get_str( //
278 scanner_t* scanner
279) {
280 return scanner->msg->buf + scanner->cursor;
281}
282
291 void* param1,
292 void* param2
293) {
294 assert(param1 != NULL);
295 assert(param2 != NULL);
296
297 return ((http_header_t*)param1)->name_id ==
298 ((http_header_t*)param2)->name_id;
299}
300
305 void* msg
306) {
307 http_header_t* hdr = (http_header_t*)msg;
308
311 free(hdr);
312}
313
323inline parse_status_t
325) {
327 token_type_t tok_type;
328 parse_status_t status;
329
330 /* skip ws, crlf */
331 do {
332 status = scanner_get_token(scanner, &token, &tok_type);
333 } while (status == (parse_status_t)PARSE_OK &&
334 (tok_type == (token_type_t)TT_WHITESPACE ||
335 tok_type == (token_type_t)TT_CRLF));
336 if (status == (parse_status_t)PARSE_OK) {
337 /* pushback a non-whitespace token */
338 scanner->cursor -= token.length;
339 }
340
341 return status;
342}
343
354) {
356 token_type_t tok_type;
357 parse_status_t status;
358 size_t save_pos;
359 int matched;
360
361 do {
362 save_pos = scanner->cursor;
363 matched = 0;
364
365 /* get CRLF or WS */
366 status = scanner_get_token(scanner, &token, &tok_type);
367 if (status == (parse_status_t)PARSE_OK) {
368 if (tok_type == (token_type_t)TT_CRLF) {
369 /* get WS */
370 status = scanner_get_token(scanner, &token, &tok_type);
371 }
372
373 if (status == (parse_status_t)PARSE_OK &&
374 tok_type == (token_type_t)TT_WHITESPACE) {
375 matched = 1;
376 } else {
377 /* did not match LWS; pushback token(s) */
378 scanner->cursor = save_pos;
379 }
380 }
381 } while (matched);
382
383 /* if entire msg is loaded, ignore an 'incomplete' warning */
384 if (status == (parse_status_t)PARSE_INCOMPLETE &&
385 scanner->entire_msg_loaded) {
386 status = PARSE_OK;
387 }
388
389 return status;
390}
391
403 scanner_t* scanner,
404 memptr* str
405) {
406 memptr token{};
407 token_type_t tok_type;
408 parse_status_t status{};
409 int done = 0;
410 size_t save_cursor;
411
412 save_cursor = scanner->cursor;
413
414 str->length = (size_t)0;
415 str->buf = scanner_get_str(scanner); /* point to next char in input */
416
417 while (!done) {
418 status = scanner_get_token(scanner, &token, &tok_type);
419 if (status == (parse_status_t)PARSE_OK &&
420 tok_type != (token_type_t)TT_WHITESPACE &&
421 tok_type != (token_type_t)TT_CRLF) {
422 /* append non-ws token */
423 str->length += token.length;
424 } else {
425 done = 1;
426 }
427 }
428
429 if (status == (parse_status_t)PARSE_OK) {
430 /* last token was WS; push it back in */
431 scanner->cursor -= token.length;
432 }
433 /* tolerate 'incomplete' msg */
434 if (status == (parse_status_t)PARSE_OK ||
435 (status == (parse_status_t)PARSE_INCOMPLETE &&
436 scanner->entire_msg_loaded)) {
437 if (str->length == (size_t)0) {
438 /* no strings found */
439 return PARSE_NO_MATCH;
440 } else {
441 return PARSE_OK;
442 }
443 } else {
444 /* error -- pushback tokens */
445 scanner->cursor = save_cursor;
446 return status;
447 }
448}
449
462inline parse_status_t
464 memptr* raw_value
465) {
467 token_type_t tok_type;
468 parse_status_t status{};
469 int done = 0;
470 int saw_crlf = 0;
471 size_t pos_at_crlf = (size_t)0;
472 size_t save_pos;
473 char c;
474
475 save_pos = scanner->cursor;
476
477 /* value points to start of input */
478 raw_value->buf = scanner_get_str(scanner);
479 raw_value->length = (size_t)0;
480
481 while (!done) {
482 status = scanner_get_token(scanner, &token, &tok_type);
483 if (status == (parse_status_t)PARSE_OK) {
484 if (!saw_crlf) {
485 if (tok_type == (token_type_t)TT_CRLF) {
486 /* CRLF could end value */
487 saw_crlf = 1;
488
489 /* save input position at start of CRLF
490 */
491 pos_at_crlf = scanner->cursor - token.length;
492 }
493 /* keep appending value */
494 raw_value->length += token.length;
495 } else /* already seen CRLF */
496 {
497 if (tok_type == (token_type_t)TT_WHITESPACE) {
498 /* start again; forget CRLF */
499 saw_crlf = 0;
500 raw_value->length += token.length;
501 } else {
502 /* non-ws means value ended just before
503 * CRLF */
504 done = 1;
505
506 /* point to the crlf which ended the
507 * value */
508 scanner->cursor = pos_at_crlf;
509 }
510 }
511 } else {
512 /* some kind of error; restore scanner position */
513 scanner->cursor = save_pos;
514 done = 1;
515 }
516 }
517
518 if (status == (parse_status_t)PARSE_OK) {
519 /* trim whitespace on right side of value */
520 while (raw_value->length > (size_t)0) {
521 /* get last char */
522 c = raw_value->buf[raw_value->length - (size_t)1];
523
524 if (c != ' ' && c != '\t' && c != TOKCHAR_CR && c != TOKCHAR_LF) {
525 /* done; no more whitespace */
526 break;
527 }
528 /* remove whitespace */
529 raw_value->length--;
530 }
531 }
532
533 return status;
534}
535
550 scanner_t* scanner,
551 int base,
552 int* value
553) {
555 token_type_t tok_type;
556 parse_status_t status;
557 long num;
558 char* end_ptr;
559 size_t save_pos;
560
561 save_pos = scanner->cursor;
562 status = scanner_get_token(scanner, &token, &tok_type);
563 if (status == (parse_status_t)PARSE_OK) {
564 if (tok_type == (token_type_t)TT_IDENTIFIER) {
565 errno = 0;
566 num = strtol(token.buf, &end_ptr, base);
567 /* all and only those chars in token should be used for
568 * num */
569 if (num < 0 || end_ptr != token.buf + token.length ||
570 ((num == LONG_MIN || num == LONG_MAX) && (errno == ERANGE))) {
571 status = PARSE_NO_MATCH;
572 }
573 /* save result */
574 *value = (int)num;
575 } else {
576 /* token must be an identifier */
577 status = PARSE_NO_MATCH;
578 }
579 }
580 if (status != (parse_status_t)PARSE_OK) {
581 /* restore scanner position for bad values */
582 scanner->cursor = save_pos;
583 }
584
585 return status;
586}
587
601 scanner_t* scanner,
602 memptr* str
603) {
605 token_type_t tok_type;
606 parse_status_t status;
607 size_t start_pos;
608
609 start_pos = scanner->cursor;
610 str->buf = scanner_get_str(scanner);
611
612 /* read until we hit a crlf */
613 do {
614 status = scanner_get_token(scanner, &token, &tok_type);
615 } while (status == (parse_status_t)PARSE_OK &&
616 tok_type != (token_type_t)TT_CRLF);
617
618 if (status == (parse_status_t)PARSE_OK) {
619 /* pushback crlf in stream */
620 scanner->cursor -= token.length;
621
622 /* str should include all strings except crlf at the end */
623 str->length = scanner->cursor - start_pos;
624 }
625
626 return status;
627}
628
640inline parse_status_t
642 char c,
643 int case_sensitive
645) {
646 char scan_char;
647
648 if (scanner->cursor >= scanner->msg->length) {
649 return PARSE_INCOMPLETE;
650 }
651 /* read next char from scanner */
652 scan_char = scanner->msg->buf[scanner->cursor++];
653
654 if (case_sensitive) {
655 return c == scan_char ? PARSE_OK : PARSE_NO_MATCH;
656 } else {
657 return tolower(c) == tolower(scan_char) ? PARSE_OK : PARSE_NO_MATCH;
658 }
659}
660
661/*
662 *
663 *
664 * args for ...
665 * %d, int * (31-bit positive integer)
666 * %x, int * (31-bit postive number encoded as hex)
667 * %s, memptr* (simple identifier)
668 * %q, memptr* (quoted string)
669 * %S, memptr* (non-whitespace string)
670 * %R, memptr* (raw value)
671 * %U, uri_type* (url)
672 * %L, memptr* (string until end of line)
673 * %P, int * (current index of the string being scanned)
674 *
675 * no args for
676 * ' ' LWS*
677 * \t whitespace
678 * "%%" matches '%'
679 * "% " matches ' '
680 * %c matches CRLF
681 * %i ignore case in literal matching
682 * %n case-sensitive matching in literals
683 * %w optional whitespace; (similar to '\t',
684 * except whitespace is optional)
685 * %0 (zero) match null-terminator char '\0'
686 * (can only be used as last char in fmt)
687 * use only in matchstr(), not match()
688 * other chars match literally
689 *
690 * returns:
691 * PARSE_OK
692 * PARSE_INCOMPLETE
693 * PARSE_FAILURE -- bad input
694 * PARSE_NO_MATCH -- input does not match pattern
695 */
696
711 scanner_t* scanner,
712 const char* fmt,
713 va_list argp
714) {
715 char c;
716 const char* fmt_ptr = fmt;
717 parse_status_t status{};
718 memptr* str_ptr;
719 memptr temp_str;
720 int* int_ptr;
721 uri_type* uri_ptr;
722 size_t save_pos;
723 int stat;
724 int case_sensitive = 1;
726 token_type_t tok_type;
727 int base;
728
729 assert(scanner != NULL);
730 assert(fmt != NULL);
731
732 /* save scanner pos; to aid error recovery */
733 save_pos = scanner->cursor;
734
735 status = PARSE_OK;
736 while (((c = *fmt_ptr++) != 0) && (status == (parse_status_t)PARSE_OK)) {
737 if (c == '%') {
738 c = *fmt_ptr++;
739 switch (c) {
740 case 'R': /* raw value */
741 str_ptr = va_arg(argp, memptr*);
742 assert(str_ptr != NULL);
743 status = match_raw_value(scanner, str_ptr);
744 break;
745 case 's': /* simple identifier */
746 str_ptr = va_arg(argp, memptr*);
747 assert(str_ptr != NULL);
748 status = scanner_get_token(scanner, str_ptr, &tok_type);
749 if (status == (parse_status_t)PARSE_OK &&
750 tok_type != (token_type_t)TT_IDENTIFIER) {
751 /* not an identifier */
752 status = PARSE_NO_MATCH;
753 }
754 break;
755 case 'c': /* crlf */
756 status = scanner_get_token(scanner, &token, &tok_type);
757 if (status == (parse_status_t)PARSE_OK &&
758 tok_type != (token_type_t)TT_CRLF) {
759 /* not CRLF token */
760 status = PARSE_NO_MATCH;
761 }
762 break;
763 case 'd': /* integer */
764 case 'x': /* hex number */
765 int_ptr = va_arg(argp, int*);
766 assert(int_ptr != NULL);
767 base = c == 'd' ? 10 : 16;
768 status = match_int(scanner, base, int_ptr);
769 break;
770 case 'S': /* non-whitespace string */
771 case 'U': /* uri */
772 if (c == 'S') {
773 str_ptr = va_arg(argp, memptr*);
774 } else {
775 str_ptr = &temp_str;
776 }
777 assert(str_ptr != NULL);
778 status = match_non_ws_string(scanner, str_ptr);
779 if (c == 'U' && status == (parse_status_t)PARSE_OK) {
780 uri_ptr = va_arg(argp, uri_type*);
781 assert(uri_ptr != NULL);
782 stat = parse_uri(str_ptr->buf, str_ptr->length, uri_ptr);
783 if (stat != HTTP_SUCCESS) {
784 status = PARSE_NO_MATCH;
785 }
786 }
787 break;
788 case 'L': /* string till eol */
789 str_ptr = va_arg(argp, memptr*);
790 assert(str_ptr != NULL);
791 status = read_until_crlf(scanner, str_ptr);
792 break;
793 case ' ': /* match space */
794 case '%': /* match percentage symbol */
795 status = match_char(scanner, c, case_sensitive);
796 break;
797 case 'n': /* case-sensitive match */
798 case_sensitive = 1;
799 break;
800 case 'i': /* ignore case */
801 case_sensitive = 0;
802 break;
803 case 'q': /* quoted string */
804 str_ptr = (memptr*)va_arg(argp, memptr*);
805 status = scanner_get_token(scanner, str_ptr, &tok_type);
806 if (status == (parse_status_t)PARSE_OK &&
807 tok_type != (token_type_t)TT_QUOTEDSTRING) {
808 status = PARSE_NO_MATCH; /* not a quoted
809 string */
810 }
811 break;
812 case 'w':
813 /* optional whitespace */
814 status = scanner_get_token(scanner, &token, &tok_type);
815 if (status == (parse_status_t)PARSE_OK &&
816 tok_type != (token_type_t)TT_WHITESPACE) {
817 /* restore non-whitespace token */
818 scanner->cursor -= token.length;
819 }
820 break;
821 case 'P':
822 /* current pos of scanner */
823 int_ptr = va_arg(argp, int*);
824 assert(int_ptr != NULL);
825 *int_ptr = (int)scanner->cursor;
826 break;
827 /* valid only in matchstr() */
828 case '0':
829 /* end of msg? */
830 /* check that we are 1 beyond last char */
831 if (scanner->cursor == scanner->msg->length &&
832 scanner->msg->buf[scanner->cursor] == '\0') {
833 status = PARSE_OK;
834 } else {
835 status = PARSE_NO_MATCH;
836 }
837 break;
838 default:
839 /* unknown option */
840 assert(0);
841 }
842 } else {
843 switch (c) {
844 case ' ': /* LWS* */
845 status = skip_lws(scanner);
846 break;
847 case '\t': /* Whitespace */
848 status = scanner_get_token(scanner, &token, &tok_type);
849 if (status == (parse_status_t)PARSE_OK &&
850 tok_type != (token_type_t)TT_WHITESPACE) {
851 /* not whitespace token */
852 status = PARSE_NO_MATCH;
853 }
854 break;
855 default: /* match characters */
856 {
857 status = match_char(scanner, c, case_sensitive);
858 }
859 }
860 }
861 }
862 if (status != (parse_status_t)PARSE_OK) {
863 /* on error, restore original scanner pos */
864 scanner->cursor = save_pos;
865 }
866
867 return status;
868}
869
883 scanner_t* scanner,
884 const char* fmt,
885 ...
886) {
887 parse_status_t ret_code;
888 va_list args;
889
890 va_start(args, fmt);
891 ret_code = vfmatch(scanner, fmt, args);
892 va_end(args);
893
894 return ret_code;
895}
896
901 http_message_t* msg
902) {
903 TRACE("Executing httpmsg_init()")
904 msg->entity.buf = nullptr;
905 msg->entity.length = (size_t)0;
907 membuffer_init(&msg->msg);
909 msg->urlbuf = nullptr;
910 msg->initialized = 1;
911}
912
916inline void parser_init( //
917 http_parser_t* parser
918) {
919 if (parser == nullptr)
920 return;
921
922 memset(parser, 0, sizeof(http_parser_t));
923 parser->http_error_code = HTTP_BAD_REQUEST; /* err msg by default */
925 parser->valid_ssdp_notify_hack = 0;
926
927 httpmsg_init(&parser->msg);
928 scanner_init(&parser->scanner, &parser->msg.msg);
929}
930
943 http_parser_t* parser
944) {
945 parse_status_t status;
946 http_message_t* hmsg = &parser->msg;
947 memptr method_str;
948 memptr version_str;
949 size_t index;
950 char save_char;
951 int num_scanned;
952 memptr url_str;
953
954 assert(parser->position == POS_REQUEST_LINE);
955
956 status = skip_blank_lines(&parser->scanner);
957 if (status != (parse_status_t)PARSE_OK) {
958 return status;
959 }
960 /* simple get http 0.9 as described in http 1.0 spec */
961
962 status = match(&parser->scanner, "%s\t%S%w%c", &method_str, &url_str);
963
964 UPnPsdk::CStrIntMap http_method_table(Http_Method_Table);
965 if (status == (parse_status_t)PARSE_OK) {
966 index = http_method_table.index_of(method_str.buf, true);
967#if 0
968 index =
969 map_str_to_int(method_str.buf, method_str.length,
970 &Http_Method_Table[0], Http_Method_Table.size(), 1);
971#endif
972 if (index == http_method_table.npos) {
973 /* error; method not found */
974 parser->http_error_code = HTTP_NOT_IMPLEMENTED;
975 return PARSE_FAILURE;
976 }
977
978 if (Http_Method_Table[index].id != HTTPMETHOD_GET) {
979 parser->http_error_code = HTTP_BAD_REQUEST;
980 return PARSE_FAILURE;
981 }
982
983 hmsg->method = HTTPMETHOD_SIMPLEGET;
984
985 /* remove excessive leading slashes, keep one slash */
986 while (url_str.length >= 2 && url_str.buf[0] == '/' &&
987 url_str.buf[1] == '/') {
988 url_str.buf++;
989 url_str.length--;
990 }
991 /* store url */
992 hmsg->urlbuf = str_alloc(url_str.buf, url_str.length);
993 if (hmsg->urlbuf == NULL) {
994 /* out of mem */
995 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
996 return PARSE_FAILURE;
997 }
998 if (parse_uri(hmsg->urlbuf, url_str.length, &hmsg->uri) !=
999 HTTP_SUCCESS) {
1000 return PARSE_FAILURE;
1001 }
1002
1003 parser->position = POS_COMPLETE; /* move to headers */
1004
1005 return PARSE_SUCCESS;
1006 }
1007
1008 status = match(&parser->scanner, "%s\t%S\t%ihttp%w/%w%L%c", &method_str,
1009 &url_str, &version_str);
1010 if (status != (parse_status_t)PARSE_OK) {
1011 return status;
1012 }
1013 /* remove excessive leading slashes, keep one slash */
1014 while (url_str.length >= 2 && url_str.buf[0] == '/' &&
1015 url_str.buf[1] == '/') {
1016 url_str.buf++;
1017 url_str.length--;
1018 }
1019 /* store url */
1020 hmsg->urlbuf = str_alloc(url_str.buf, url_str.length);
1021 if (hmsg->urlbuf == NULL) {
1022 /* out of mem */
1023 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1024 return PARSE_FAILURE;
1025 }
1026 if (parse_uri(hmsg->urlbuf, url_str.length, &hmsg->uri) != HTTP_SUCCESS) {
1027 return PARSE_FAILURE;
1028 }
1029#if 0
1030 index = map_str_to_int(method_str.buf, method_str.length, &Http_Method_Table[0],
1031 Http_Method_Table.size(), 1);
1032#endif
1033 // Valid content of method_str.buf is not terminated with '\0' but only
1034 // specified by the length.
1035 const std::string tmpbuf = std::string(method_str.buf, method_str.length);
1036 index = http_method_table.index_of(tmpbuf.c_str(), true);
1037
1038 if (index == http_method_table.npos) {
1039 /* error; method not found */
1040 parser->http_error_code = HTTP_NOT_IMPLEMENTED;
1041 return PARSE_FAILURE;
1042 }
1043
1044 /* scan version */
1045 save_char = version_str.buf[version_str.length];
1046 version_str.buf[version_str.length] = '\0'; /* null-terminate */
1047#ifdef _WIN32
1048 num_scanned =
1049 sscanf_s(version_str.buf,
1050#else
1051 num_scanned = sscanf(version_str.buf,
1052#endif
1053 "%d . %d", &hmsg->major_version, &hmsg->minor_version);
1054 version_str.buf[version_str.length] = save_char; /* restore */
1055 if (num_scanned != 2 ||
1056 /* HTTP version equals to 1.0 should fail for MSEARCH as
1057 * required by the UPnP certification tool */
1058 hmsg->major_version < 0 ||
1059 (hmsg->major_version == 1 && hmsg->minor_version < 1 &&
1060 Http_Method_Table[index].id == HTTPMETHOD_MSEARCH)) {
1061 parser->http_error_code = HTTP_HTTP_VERSION_NOT_SUPPORTED;
1062 /* error; bad http version */
1063 return PARSE_FAILURE;
1064 }
1065
1066 hmsg->method = (http_method_t)Http_Method_Table[index].id;
1067 parser->position = POS_HEADERS; /* move to headers */
1068
1069 return PARSE_OK;
1070}
1071
1080 http_parser_t* parser
1081) {
1082 /*int entity_length; */
1083
1084 assert(parser->ent_position == ENTREAD_USING_CLEN);
1085
1086 /* determine entity (i.e. body) length so far */
1087 parser->msg.entity.length = parser->msg.msg.length -
1088 parser->entity_start_position +
1089 parser->msg.amount_discarded;
1090
1091 if (parser->msg.entity.length < parser->content_length) {
1092 /* more data to be read */
1093 return PARSE_INCOMPLETE;
1094 } else {
1095 if (parser->msg.entity.length > parser->content_length) {
1096 /* silently discard extra data */
1097 parser->msg.msg
1098 .buf[parser->entity_start_position + parser->content_length -
1099 parser->msg.amount_discarded] = '\0';
1100 }
1101 /* save entity length */
1102 parser->msg.entity.length = parser->content_length;
1103
1104 /* save entity start ptr; (the very last thing to do) */
1105 parser->msg.entity.buf =
1106 parser->msg.msg.buf + parser->entity_start_position;
1107
1108 /* done reading entity */
1109 parser->position = POS_COMPLETE;
1110 return PARSE_SUCCESS;
1111 }
1112}
1113
1124inline parse_status_t
1126) {
1127 parse_status_t status;
1128 size_t save_pos;
1129
1130 /* if 'chunk_size' of bytes have been read; read next chunk */
1131 if ((parser->msg.msg.length - parser->scanner.cursor) >=
1132 parser->chunk_size) {
1133 /* move to next chunk */
1134 parser->scanner.cursor += parser->chunk_size;
1135 save_pos = parser->scanner.cursor;
1136 /* discard CRLF */
1137 status = match(&parser->scanner, "%c");
1138 if (status != (parse_status_t)PARSE_OK) {
1139 /*move back */
1140 parser->scanner.cursor -= parser->chunk_size;
1141 /*parser->scanner.cursor = save_pos; */
1142 return status;
1143 }
1144 membuffer_delete(&parser->msg.msg, save_pos,
1145 (parser->scanner.cursor - save_pos));
1146 parser->scanner.cursor = save_pos;
1147 /*update temp */
1148 parser->msg.entity.length += parser->chunk_size;
1150 return PARSE_CONTINUE_1;
1151 } else
1152 /* need more data for chunk */
1153 return PARSE_INCOMPLETE;
1154}
1155
1167 http_parser_t* parser
1168) {
1169 parse_status_t status;
1170 size_t save_pos;
1171
1172 save_pos = parser->scanner.cursor;
1173 status = parser_parse_headers(parser);
1174 if (status == (parse_status_t)PARSE_OK) {
1175 /* finally, done with the whole msg */
1176 parser->position = POS_COMPLETE;
1177
1178 membuffer_delete(&parser->msg.msg, save_pos,
1179 (parser->scanner.cursor - save_pos));
1180 parser->scanner.cursor = save_pos;
1181
1182 /* save entity start ptr as the very last thing to do */
1183 parser->msg.entity.buf =
1184 parser->msg.msg.buf + parser->entity_start_position;
1185
1186 return PARSE_SUCCESS;
1187 } else {
1188 return status;
1189 }
1190}
1191
1203 http_parser_t* parser
1204) {
1205 scanner_t* scanner = &parser->scanner;
1206 parse_status_t status;
1207 size_t save_pos;
1208 memptr dummy;
1209
1210 assert(parser->ent_position == ENTREAD_USING_CHUNKED);
1211
1212 save_pos = scanner->cursor;
1213
1214 /* get size of chunk, discard extension, discard CRLF */
1215 status = match(scanner, "%x%L%c", &parser->chunk_size, &dummy);
1216 if (status != (parse_status_t)PARSE_OK) {
1217 scanner->cursor = save_pos;
1218 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1219 "CHUNK COULD NOT BE PARSED\n");
1220 return status;
1221 }
1222 /* remove chunk info just matched; just retain data */
1223 membuffer_delete(&parser->msg.msg, save_pos, (scanner->cursor - save_pos));
1224 scanner->cursor = save_pos; /* adjust scanner too */
1225
1226 if (parser->chunk_size == (size_t)0) {
1227 /* done reading entity; determine length of entity */
1228 parser->msg.entity.length = parser->scanner.cursor -
1229 parser->entity_start_position +
1230 parser->msg.amount_discarded;
1231
1232 /* read entity headers */
1234 } else {
1235 /* read chunk body */
1237 }
1238
1239 return PARSE_CONTINUE_1; /* continue to reading body */
1240}
1241
1248 http_parser_t* parser
1249) {
1250 size_t cursor;
1251
1252 assert(parser->ent_position == ENTREAD_UNTIL_CLOSE);
1253
1254 /* eat any and all data */
1255 cursor = parser->msg.msg.length;
1256
1257 /* update entity length */
1258 parser->msg.entity.length =
1259 cursor - parser->entity_start_position + parser->msg.amount_discarded;
1260
1261 /* update pointer */
1262 parser->msg.entity.buf =
1263 parser->msg.msg.buf + parser->entity_start_position;
1264
1265 parser->scanner.cursor = cursor;
1266
1267 return PARSE_INCOMPLETE_ENTITY; /* add anything */
1268}
1269
1270} // anonymous namespace
1271
1272
1274 assert(msg != NULL);
1275
1276 if (msg->initialized == 1) {
1277 ListDestroy(&msg->headers, 1);
1278 membuffer_destroy(&msg->msg);
1280 free(msg->urlbuf);
1281 msg->initialized = 0;
1282 }
1283}
1284
1285
1287 const char* header_name) {
1288 http_header_t* header;
1289
1290 ListNode* node;
1291
1292 node = ListHead(&msg->headers);
1293 while (node != NULL) {
1294
1295 header = (http_header_t*)node->item;
1296
1297 if (memptr_cmp_nocase(&header->name, header_name) == 0) {
1298 return header;
1299 }
1300
1301 node = ListNext(&msg->headers, node);
1302 }
1303 return NULL;
1304}
1305
1306
1308 memptr* value) {
1309 http_header_t header; /* temp header for searching */
1310 ListNode* node;
1311 http_header_t* data;
1312
1313 header.name_id = header_name_id;
1314 node = ListFind(&msg->headers, NULL, &header);
1315 if (node == NULL) {
1316 return NULL;
1317 }
1318 data = (http_header_t*)node->item;
1319 if (value != NULL) {
1320 value->buf = data->value.buf;
1321 value->length = data->value.length;
1322 }
1323
1324 return data;
1325}
1326
1327
1328parse_status_t matchstr(char* str, size_t slen, const char* fmt, ...) {
1329 parse_status_t ret_code;
1330 char save_char;
1331 scanner_t scanner;
1332 membuffer buf;
1333 va_list arg_list;
1334
1335 /* null terminate str */
1336 save_char = str[slen];
1337 str[slen] = '\0';
1338
1339 membuffer_init(&buf);
1340
1341 /* under no circumstances should this buffer be modifed because its
1342 * memory */
1343 /* might have not come from malloc() */
1344 membuffer_attach(&buf, str, slen);
1345
1346 scanner_init(&scanner, &buf);
1347 scanner.entire_msg_loaded = 1;
1348
1349 va_start(arg_list, fmt);
1350 ret_code = vfmatch(&scanner, fmt, arg_list);
1351 va_end(arg_list);
1352
1353 /* restore str */
1354 str[slen] = save_char;
1355
1356 /* don't destroy buf */
1357
1358 return ret_code;
1359}
1360
1361
1363 parse_status_t status;
1364 http_message_t* hmsg = &parser->msg;
1365 memptr line;
1366 char save_char;
1367 int num_scanned;
1368 int i;
1369 size_t n;
1370 char* p;
1371
1372 assert(parser->position == POS_RESPONSE_LINE);
1373
1374 status = skip_blank_lines(&parser->scanner);
1375 if (status != (parse_status_t)PARSE_OK)
1376 return status;
1377 /* response line */
1378 /*status = match( &parser->scanner, "%ihttp%w/%w%d\t.\t%d\t%d\t%L%c", */
1379 /* &hmsg->major_version, &hmsg->minor_version, */
1380 /* &hmsg->status_code, &hmsg->status_msg ); */
1381 status = match(&parser->scanner, "%ihttp%w/%w%L%c", &line);
1382 if (status != (parse_status_t)PARSE_OK)
1383 return status;
1384 save_char = line.buf[line.length];
1385 line.buf[line.length] = '\0'; /* null-terminate */
1386 /* scan http version and ret code */
1387#ifdef _WIN32
1388 num_scanned = sscanf_s(line.buf,
1389#else
1390 num_scanned = sscanf(line.buf,
1391#endif
1392 "%d . %d %d", &hmsg->major_version,
1393 &hmsg->minor_version, &hmsg->status_code);
1394 line.buf[line.length] = save_char; /* restore */
1395 if (num_scanned != 3 || hmsg->major_version < 0 ||
1396 hmsg->minor_version < 0 || hmsg->status_code < 0)
1397 /* bad response line */
1398 return PARSE_FAILURE;
1399 /* point to status msg */
1400 p = line.buf;
1401 /* skip 3 ints */
1402 for (i = 0; i < 3; i++) {
1403 /* go to start of num */
1404 while (!isdigit(*p))
1405 p++;
1406 /* skip int */
1407 while (isdigit(*p))
1408 p++;
1409 }
1410 /* whitespace must exist after status code */
1411 if (*p != ' ' && *p != '\t')
1412 return PARSE_FAILURE;
1413 /* skip whitespace */
1414 while (*p == ' ' || *p == '\t')
1415 p++;
1416 /* now, p is at start of status msg */
1417 n = line.length - ((size_t)p - (size_t)line.buf);
1418 if (membuffer_assign(&hmsg->status_msg, p, n) != 0) {
1419 /* out of mem */
1420 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1421 return PARSE_FAILURE;
1422 }
1423 parser->position = POS_HEADERS; /* move to headers */
1424
1425 return PARSE_OK;
1426}
1427
1428
1430 parse_status_t status;
1431 memptr token;
1432 memptr hdr_value;
1433 token_type_t tok_type;
1434 scanner_t* scanner = &parser->scanner;
1435 size_t save_pos;
1436 http_header_t* header;
1437 int header_id;
1438 int ret = 0;
1439 size_t index;
1440 http_header_t* orig_header;
1441 char save_char;
1442 int ret2;
1443 static char zero = 0;
1444
1445 assert(parser->position == (parser_pos_t)POS_HEADERS ||
1447
1448 while (1) {
1449 save_pos = scanner->cursor;
1450 /* check end of headers */
1451 status = scanner_get_token(scanner, &token, &tok_type);
1452 if (status != (parse_status_t)PARSE_OK) {
1453 /* pushback tokens; useful only on INCOMPLETE error */
1454 scanner->cursor = save_pos;
1455 return status;
1456 }
1457 switch (tok_type) {
1458 case TT_CRLF:
1459 /* end of headers */
1460 if ((parser->msg.is_request) &&
1461 (parser->msg.method == (http_method_t)HTTPMETHOD_POST)) {
1462 parser->position = POS_COMPLETE; /*post entity parsing */
1463 /*is handled separately */
1464 return PARSE_SUCCESS;
1465 }
1466 parser->position = POS_ENTITY; /* read entity next */
1467 return PARSE_OK;
1468 case TT_IDENTIFIER:
1469 /* not end; read header */
1470 break;
1471 default:
1472 return PARSE_FAILURE; /* didn't see header name */
1473 }
1474 status = match(scanner, " : %R%c", &hdr_value);
1475 if (status != (parse_status_t)PARSE_OK) {
1476 /* pushback tokens; useful only on INCOMPLETE error */
1477 scanner->cursor = save_pos;
1478 return status;
1479 }
1480 /* add header */
1481 /* find header */
1482
1483 UPnPsdk::CStrIntMap http_header_names_table(Http_Header_Names);
1484 index = http_header_names_table.index_of(
1485 std::string(token.buf, token.length).c_str());
1486 if (index != http_header_names_table.npos) {
1487 /*Check if it is a soap header */
1488 if (Http_Header_Names[index].id == HDR_SOAPACTION) {
1489 parser->msg.method = SOAPMETHOD_POST;
1490 }
1491 header_id = Http_Header_Names[index].id;
1492 orig_header = httpmsg_find_hdr(&parser->msg, header_id, NULL);
1493 } else {
1494 header_id = HDR_UNKNOWN;
1495 save_char = token.buf[token.length];
1496 token.buf[token.length] = '\0';
1497 orig_header = httpmsg_find_hdr_str(&parser->msg, token.buf);
1498 token.buf[token.length] = save_char; /* restore */
1499 }
1500 if (orig_header == NULL) {
1501 /* add new header */
1502 header = (http_header_t*)malloc(sizeof(http_header_t));
1503 if (header == NULL) {
1504 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1505 return PARSE_FAILURE;
1506 }
1507 membuffer_init(&header->name_buf);
1508 membuffer_init(&header->value);
1509 /* value can be 0 length */
1510 if (hdr_value.length == (size_t)0) {
1511 hdr_value.buf = &zero;
1512 hdr_value.length = (size_t)1;
1513 }
1514 /* save in header in buffers */
1515 if (membuffer_assign(&header->name_buf, token.buf, token.length) ||
1516 membuffer_assign(&header->value, hdr_value.buf,
1517 hdr_value.length)) {
1518 /* not enough mem */
1519 membuffer_destroy(&header->value);
1520 membuffer_destroy(&header->name_buf);
1521 free(header);
1522 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1523 return PARSE_FAILURE;
1524 }
1525 header->name.buf = header->name_buf.buf;
1526 header->name.length = header->name_buf.length;
1527 header->name_id = header_id;
1528 if (!ListAddTail(&parser->msg.headers, header)) {
1529 membuffer_destroy(&header->value);
1530 membuffer_destroy(&header->name_buf);
1531 free(header);
1532 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1533 return PARSE_FAILURE;
1534 }
1535 } else if (hdr_value.length > (size_t)0) {
1536 /* append value to existing header */
1537 /* append space */
1538 ret = membuffer_append_str(&orig_header->value, ", ");
1539 /* append continuation of header value */
1540 ret2 = membuffer_append(&orig_header->value, hdr_value.buf,
1541 hdr_value.length);
1542 if (ret == UPNP_E_OUTOF_MEMORY || ret2 == UPNP_E_OUTOF_MEMORY) {
1543 /* not enuf mem */
1544 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1545 return PARSE_FAILURE;
1546 }
1547 }
1548 } /* end while */
1549}
1550
1551
1553 http_message_t* hmsg = &parser->msg;
1554 int response_code;
1555 memptr hdr_value;
1556
1558
1559 /* entity points to start of msg body */
1560 parser->msg.entity.buf = scanner_get_str(&parser->scanner);
1561 parser->msg.entity.length = (size_t)0;
1562
1563 /* remember start of body */
1564 parser->entity_start_position = parser->scanner.cursor;
1565
1566 /* std http rules for determining content length */
1567
1568 /* * no body for 1xx, 204, 304 and HEAD, GET, */
1569 /* SUBSCRIBE, UNSUBSCRIBE */
1570 if (hmsg->is_request) {
1571 switch (hmsg->method) {
1572 case HTTPMETHOD_HEAD:
1573 case HTTPMETHOD_GET:
1574 /*case HTTPMETHOD_POST: */
1575 case HTTPMETHOD_SUBSCRIBE:
1576 case HTTPMETHOD_UNSUBSCRIBE:
1577 case HTTPMETHOD_MSEARCH:
1578 /* no body; mark as done */
1579 parser->position = POS_COMPLETE;
1580 return PARSE_SUCCESS;
1581 break;
1582
1583 default:; /* do nothing */
1584 }
1585 } else /* response */
1586 {
1587 response_code = hmsg->status_code;
1588
1589 if (response_code == 204 || response_code == 304 ||
1590 (response_code >= 100 && response_code <= 199) ||
1591 hmsg->request_method == (http_method_t)HTTPMETHOD_HEAD ||
1592 hmsg->request_method == (http_method_t)HTTPMETHOD_MSEARCH ||
1593 hmsg->request_method == (http_method_t)HTTPMETHOD_SUBSCRIBE ||
1594 hmsg->request_method == (http_method_t)HTTPMETHOD_UNSUBSCRIBE ||
1595 hmsg->request_method == (http_method_t)HTTPMETHOD_NOTIFY) {
1596 parser->position = POS_COMPLETE;
1597 return PARSE_SUCCESS;
1598 }
1599 }
1600
1601 /* * transfer-encoding -- used to indicate chunked data */
1602 if (httpmsg_find_hdr(hmsg, HDR_TRANSFER_ENCODING, &hdr_value)) {
1603 if (raw_find_str(&hdr_value, "chunked") >= 0) {
1604 /* read method to use chunked transfer encoding */
1606 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1607 "Found Chunked Encoding ....\n");
1608
1609 return PARSE_CONTINUE_1;
1610 }
1611 }
1612 /* * use content length */
1613 if (httpmsg_find_hdr(hmsg, HDR_CONTENT_LENGTH, &hdr_value)) {
1614 parser->content_length = (unsigned int)raw_to_int(&hdr_value, 10);
1616 return PARSE_CONTINUE_1;
1617 }
1618 /* * multi-part/byteranges not supported (yet) */
1619
1620 /* * read until connection is closed */
1621 if (hmsg->is_request) {
1622 /* set hack flag for NOTIFY methods; if set to 1 this is */
1623 /* a valid SSDP notify msg */
1624 if (hmsg->method == (http_method_t)HTTPMETHOD_NOTIFY) {
1625 parser->valid_ssdp_notify_hack = 1;
1626 }
1627
1628 parser->http_error_code = HTTP_LENGTH_REQUIRED;
1629 return PARSE_FAILURE;
1630 }
1631
1633 return PARSE_CONTINUE_1;
1634}
1635
1636
1638 parse_status_t status;
1639
1640 assert(parser->position == POS_ENTITY);
1641
1642 do {
1643 switch (parser->ent_position) {
1644 case ENTREAD_USING_CLEN:
1645 status = parser_parse_entity_using_clen(parser);
1646 break;
1647
1649 status = parser_parse_chunky_entity(parser);
1650 break;
1651
1653 status = parser_parse_chunky_body(parser);
1654 break;
1655
1657 status = parser_parse_chunky_headers(parser);
1658 break;
1659
1661 status = parser_parse_entity_until_close(parser);
1662 break;
1663
1665 status = parser_get_entity_read_method(parser);
1666 break;
1667
1668 default:
1669 status = PARSE_FAILURE;
1670 assert(0);
1671 }
1672
1673 } while (status == (parse_status_t)PARSE_CONTINUE_1);
1674
1675 return status;
1676}
1677
1678
1680 parser_init(parser);
1681 parser->msg.is_request = 1;
1682 parser->position = POS_REQUEST_LINE;
1683}
1684
1685
1686void parser_response_init(http_parser_t* parser, http_method_t request_method) {
1687 parser_init(parser);
1688 parser->msg.is_request = 0;
1689 parser->msg.request_method = request_method;
1690 parser->msg.amount_discarded = (size_t)0;
1691 parser->position = POS_RESPONSE_LINE;
1692}
1693
1694
1696 parse_status_t status;
1697
1698 /*takes an http_parser_t with memory already allocated */
1699 /*in the message */
1700 assert(parser != nullptr);
1701
1702 do {
1703 switch (parser->position) {
1704 case POS_ENTITY:
1705 status = parser_parse_entity(parser);
1706
1707 break;
1708
1709 case POS_HEADERS:
1710 status = parser_parse_headers(parser);
1711
1712 break;
1713
1714 case POS_REQUEST_LINE:
1715 status = parser_parse_requestline(parser);
1716
1717 break;
1718
1719 case POS_RESPONSE_LINE:
1720 status = parser_parse_responseline(parser);
1721
1722 break;
1723
1724 default: {
1725 status = PARSE_FAILURE;
1726 assert(0);
1727 }
1728 }
1729
1730 } while (status == (parse_status_t)PARSE_OK);
1731
1732 return status;
1733}
1734
1735
1737 size_t buf_length) {
1738 assert(parser != nullptr);
1739 assert(buf != nullptr);
1740
1741 /* append data to buffer */
1742 int ret_code = membuffer_append(&parser->msg.msg, buf, buf_length);
1743 if (ret_code != 0) {
1744 /* set failure status */
1745 parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1746 return PARSE_FAILURE;
1747 }
1748
1749 return parser_parse(parser);
1750}
1751
1752
1753int raw_to_int(memptr* raw_value, int base) {
1754 long num;
1755 char* end_ptr;
1756
1757 if (raw_value->length == (size_t)0)
1758 return -1;
1759 errno = 0;
1760 num = strtol(raw_value->buf, &end_ptr, base);
1761 if ((num < 0)
1762 /* all and only those chars in token should be used for num */
1763 || (end_ptr != raw_value->buf + raw_value->length) ||
1764 ((num == LONG_MIN || num == LONG_MAX) && (errno == ERANGE))) {
1765 return -1;
1766 }
1767 return (int)num;
1768}
1769
1770
1771int raw_find_str(memptr* raw_value, const char* str) {
1772 char c;
1773 char* ptr;
1774 int i = 0;
1775
1776 /* save */
1777 c = raw_value->buf[raw_value->length];
1778
1779 /* Make it lowercase */
1780 for (i = 0; raw_value->buf[i]; ++i) {
1781 raw_value->buf[i] = (char)tolower(raw_value->buf[i]);
1782 }
1783
1784 /* null-terminate */
1785 raw_value->buf[raw_value->length] = 0;
1786
1787 /* Find the substring position */
1788 ptr = strstr(raw_value->buf, str);
1789
1790 /* restore the "length" byte */
1791 raw_value->buf[raw_value->length] = c;
1792
1793 if (ptr == 0) {
1794 return -1;
1795 }
1796
1797 /* return index */
1798 return (int)(ptr - raw_value->buf);
1799}
1800
1801const char* method_to_str(http_method_t method) {
1802#if 0
1803 int index =
1804 map_int_to_str(method, &Http_Method_Table[0], Http_Method_Table.size());
1805#endif
1806 UPnPsdk::CStrIntMap http_method_table(Http_Method_Table);
1807 size_t index = http_method_table.index_of(method);
1808
1809 assert(index != http_method_table.npos);
1810
1811 return index == http_method_table.npos ? NULL
1812 : Http_Method_Table[index].name;
1813}
1814
1815void print_http_headers(std::string_view log_msg, http_message_t* hmsg) {
1816 if (!UPnPsdk::g_dbug)
1817 return;
1818 UPnPsdk_LOGINFO(log_msg) "...\n";
1819
1820 ListNode* node;
1821 /* NNS: dlist_node *node; */
1822 http_header_t* header;
1823
1824 /* print start line */
1825 if (hmsg->is_request) {
1826 std::cerr << " method=" << hmsg->method
1827 << ", version=" << hmsg->major_version << "."
1828 << hmsg->minor_version << ", url=\""
1829 << std::string(hmsg->uri.pathquery.buff,
1830 hmsg->uri.pathquery.size)
1831 << "\".\n";
1832 } else {
1833 std::cerr << " resp_status=" << hmsg->status_code
1834 << ", version=" << hmsg->major_version << "."
1835 << hmsg->minor_version << ", status_msg=\""
1836 << std::string(hmsg->status_msg.buf, hmsg->status_msg.length)
1837 << "\".\n";
1838 }
1839
1840 /* print headers */
1841 node = ListHead(&hmsg->headers);
1842 /* NNS: node = dlist_first_node( &hmsg->headers ); */
1843 while (node != NULL) {
1844 header = (http_header_t*)node->item;
1845 /* NNS: header = (http_header_t *)node->data; */
1846 std::cerr << " name_id=" << header->name_id << ", hdr_name=\""
1847 << std::string(header->name.buf, header->name.length)
1848 << "\", value=\""
1849 << std::string(header->value.buf, header->value.length)
1850 << "\".\n";
1851
1852 node = ListNext(&hmsg->headers, node);
1853 /* NNS: node = dlist_next( &hmsg->headers, node ); */
1854 }
1855}
http_message_t msg
entire raw message
#define ENTREAD_CHUNKY_BODY
Defines read method.
uri_type uri
Type of a uri, e.g. absolute, relative, etc.
constexpr std::array< const UPnPsdk::str_int_entry, 33 > Http_Header_Names
Assigns header-name id to its text representation.
http_method_t method
Http method of an outgoing request.
int is_request
If 1, msg is a request, else response.
http_method_t
Method in a HTTP request.
memptr name
Header name as a string.
int major_version
Http major version.
membuffer status_msg
???
int entire_msg_loaded
Completeness of 'msg'.
#define HDR_TRANSFER_ENCODING
Type of a HTTP header.
memptr entity
message body(entity).
char * urlbuf
storage for url string.
#define ENTREAD_DETERMINE_READ_METHOD
Defines read method.
membuffer value
Raw-value; could be multi-lined; min-length = 0.
size_t amount_discarded
The amount of data that's been read by the user, that's no longer in the raw message buffer.
membuffer msg
entire raw message.
int http_error_code
read-only; in case of parse error, this contains the HTTP error code (4XX or 5XX).
membuffer name_buf
(Private use – don't touch.)
http_method_t request_method
Http method of an incoming response.
scanner_t scanner
???
size_t chunk_size
Private data – don't touch.
parser_pos_t position
Private data – don't touch.
membuffer * msg
Raw http message.
parser_pos_t
Type of a parser position.
@ POS_COMPLETE
Position complete.
@ POS_RESPONSE_LINE
Position response line.
@ POS_ENTITY
Position entity.
@ POS_REQUEST_LINE
Position request line.
@ POS_HEADERS
Position headers.
parse_status_t
Status of parsing.
@ PARSE_INCOMPLETE
@ PARSE_NO_MATCH
@ PARSE_FAILURE
@ PARSE_INCOMPLETE_ENTITY
@ PARSE_SUCCESS
@ PARSE_OK
@ PARSE_CONTINUE_1
int initialized
Indicates if the object is initialized.
unsigned int content_length
Private data – don't touch.
int valid_ssdp_notify_hack
read-only; this is set to 1 if a NOTIFY request has no content-length. used to read valid sspd notify...
size_t cursor
Current position in buffer.
#define HDR_SOAPACTION
Type of a HTTP header.
#define ENTREAD_USING_CHUNKED
Defines read method.
#define ENTREAD_USING_CLEN
Defines read method.
int name_id
Header name id (for a selective group of headers only).
int ent_position
Private data – don't touch.
LinkedList headers
List of headers.
int minor_version
Http minor version.
#define HDR_UNKNOWN
Type of a HTTP header.
#define ENTREAD_UNTIL_CLOSE
Defines read method.
size_t entity_start_position
Offset in the raw message buffer, which contains the message body. preceding this are the headers of ...
#define ENTREAD_CHUNKY_HEADERS
Defines read method.
#define HDR_CONTENT_LENGTH
Type of a HTTP header.
Structure of an HTTP parser object.
Structure of a scanner object.
Structure of an HTTP message.
Structure of an HTTP header object.
size_t size
Size of the buffer.
Definition uri.hpp:78
const char * buff
Buffer.
Definition uri.hpp:77
token pathquery
Member variable.
Definition uri.hpp:98
constexpr int HTTP_SUCCESS
Yet another success code.
Definition uri.hpp:70
Represents a URI used in parse_uri and elsewhere.
Definition uri.hpp:92
Buffer used in parsinghttp messages, urls, etc. Generally this simply holds a pointer into a larger a...
Definition uri.hpp:76
int ListDestroy(LinkedList *list, int freeItem)
Removes all memory associated with list nodes. Does not free LinkedList *list.
ListNode * ListNext(LinkedList *list, ListNode *node)
Returns the next item in the list.
ListNode * ListHead(LinkedList *list)
Returns the head of the list.
ListNode * ListAddTail(LinkedList *list, void *item)
Adds a node to the tail of the list. Node gets added immediately before list.tail.
ListNode * ListFind(LinkedList *list, ListNode *start, void *item)
Finds the specified item in the list.
int ListInit(LinkedList *list, cmp_routine cmp_func, free_function free_func)
Initializes LinkedList. Must be called first and only once for List.
Linked list node. Stores generic item and pointers to next and prev.
Table with C-strings mapped to an integer id.
Definition strintmap.hpp:38
static constexpr size_t npos
Value returned on error or when an index on a container is not found.
Definition strintmap.hpp:46
size_t index_of(const char *a_name, bool a_case_sensitive=false)
Match the given name with names from the entries in the table.
Definition strintmap.hpp:81
parse_status_t parser_parse_headers(http_parser_t *parser)
Get HTTP Method, URL location and version information.
http_header_t * httpmsg_find_hdr_str(http_message_t *msg, const char *header_name)
Compares the header name with the header names stored in the linked list of messages.
parse_status_t parser_parse(http_parser_t *parser)
The parser function.
void parser_request_init(http_parser_t *parser)
Initializes parser object for a request.
int raw_find_str(memptr *raw_value, const char *str)
Find a substring from raw character string buffer.
http_header_t * httpmsg_find_hdr(http_message_t *msg, int header_name_id, memptr *value)
Finds header from a list, with the given 'name_id'.
parse_status_t parser_parse_entity(http_parser_t *parser)
Determines method to read entity.
void parser_response_init(http_parser_t *parser, http_method_t request_method)
Initializes parser object for a response.
parse_status_t parser_parse_responseline(http_parser_t *parser)
Get HTTP Method, URL location and version information.
void httpmsg_destroy(http_message_t *msg)
Free memory allocated for the http message.
parse_status_t parser_get_entity_read_method(http_parser_t *parser)
Determines method to read entity.
int raw_to_int(memptr *raw_value, int base)
Converts raw character data to integer value.
parse_status_t parser_append(http_parser_t *parser, const char *buf, size_t buf_length)
Append date to HTTP parser, and do the parsing.
parse_status_t matchstr(char *str, size_t slen, const char *fmt,...)
Matches a variable parameter list with a string and takes actions based on the data type specified.
const char * method_to_str(http_method_t method)
A wrapper function that maps a method id to a method.
void print_http_headers(std::string_view log_msg, http_message_t *hmsg)
Print the HTTP headers.
void membuffer_destroy(membuffer *m)
Free's memory allocated for membuffer* m.
char * str_alloc(const char *str, size_t str_len)
Allocate memory and copy information from the input string to the newly allocated memory.
Definition membuffer.cpp:56
void membuffer_attach(membuffer *m, char *new_buf, size_t buf_len)
Free existing memory in membuffer and assign the new buffer in its place.
void membuffer_init(membuffer *m)
Wrapper to membuffer_initialize().
int membuffer_append(membuffer *m, const void *buf, size_t buf_len)
Invokes function to appends data from a constant buffer to the buffer.
int membuffer_append_str(membuffer *m, const char *c_str)
Invokes function to appends data from a constant string to the buffer.
int memptr_cmp_nocase(memptr *m, const char *s)
Compares characters of 2 strings irrespective of the case for a specific count of bytes.
Definition membuffer.cpp:84
int membuffer_assign(membuffer *m, const void *buf, size_t buf_len)
Allocate memory to membuffer *m and copy the contents of the in parameter const void *buf.
void membuffer_delete(membuffer *m, size_t index, size_t num_bytes)
Shrink the size of the buffer depending on the current size of the buffer and the input parameters....
size_t length
length of buffer without terminating null byte (read-only).
Definition membuffer.hpp:65
char * buf
mem buffer; must not write beyond buf[length-1] (read/write).
Definition membuffer.hpp:63
size_t length
length of memory (read-only).
Definition membuffer.hpp:54
char * buf
start of memory (read/write).
Definition membuffer.hpp:52
pointer to a chunk of memory.
Definition membuffer.hpp:50
Maintains a block of dynamically allocated memory.
Definition membuffer.hpp:61
#define UPNP_E_OUTOF_MEMORY
Not enough resources are currently available to complete the operation.
Definition messages.hpp:57
UPnPsdk_EXTERN bool g_dbug
Switch to enable verbose (debug) output.
Definition global.hpp:26
parse_status_t parser_parse_requestline(http_parser_t *parser)
Get HTTP Method, URL location and version information.
parse_status_t parser_parse_entity_using_clen(http_parser_t *parser)
Reads entity using content-length.
parse_status_t parser_parse_chunky_headers(http_parser_t *parser)
Read headers at the end of the chunked entity.
constexpr std::array< const UPnPsdk::str_int_entry, 11 > Http_Method_Table
Defines the HTTP methods.
char * scanner_get_str(scanner_t *scanner)
Get pointer to next character in string.
int is_separator_char(int c)
Determines if the passed value is a separator.
parse_status_t parser_parse_entity_until_close(http_parser_t *parser)
Keep reading entity until the connection is closed.
parse_status_t parser_parse_chunky_entity(http_parser_t *parser)
Read entity using chunked transfer encoding.
int is_identifier_char(int c)
Determines if the passed value is permissible in token.
parse_status_t read_until_crlf(scanner_t *scanner, memptr *str)
Reads data until end of line.
void httpheader_free(void *msg)
Free memory allocated for the http header.
parse_status_t match(scanner_t *scanner, const char *fmt,...)
Matches a variable parameter list and takes necessary actions based on the data type specified.
token_type_t
Used to represent different types of tokens in input.
int httpmsg_compare(void *param1, void *param2)
Compares name id in the http headers.
void httpmsg_init(http_message_t *msg)
Initialize and allocate memory for http message.
parse_status_t skip_blank_lines(scanner_t *scanner)
Skips blank lines at the start of a msg.
int is_control_char(int c)
Determines if the passed value is a control character.
parse_status_t match_int(scanner_t *scanner, int base, int *value)
Matches an unsigned integer value in the input.
parse_status_t match_raw_value(scanner_t *scanner, memptr *raw_value)
Matches a raw value in a the input.
void scanner_init(scanner_t *scanner, membuffer *bufptr)
Initialize scanner.
void parser_init(http_parser_t *parser)
Initializes the parser object.
int is_qdtext_char(int c)
Determines if the passed value is permissible in qdtext.
parse_status_t scanner_get_token(scanner_t *scanner, memptr *token, token_type_t *tok_type)
Reads next token from the input stream.
parse_status_t parser_parse_chunky_body(http_parser_t *parser)
Read data in the chunks.
parse_status_t skip_lws(scanner_t *scanner)
Skip linear whitespace.
parse_status_t match_non_ws_string(scanner_t *scanner, memptr *str)
Match a string without whitespace or CRLF (S)
parse_status_t match_char(scanner_t *scanner, char c, int case_sensitive)
Compares a character to the next char in the scanner.
parse_status_t vfmatch(scanner_t *scanner, const char *fmt, va_list argp)
Extracts variable parameters depending on the passed in format parameter.
HTTP status codes.
Define macro for synced logging to the console for detailed info and debug.
Manage Debug messages with levels "critical" to "all".
UPnPsdk_VIS void UpnpPrintf(Upnp_LogLevel DLevel, Dbg_Module Module, const char *DbgFileName, int DbgLineNo, const char *FmtStr,...)
Prints the debug statement.
int parse_uri(const char *in, size_t max, uri_type *out)
Parses a uri as defined in RFC 2396 (explaining URIs).
Definition uri.cpp:733