llhttp_url.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. #include <assert.h>
  2. #include <string.h>
  3. #include "llhttp_url.h"
  4. #ifdef __GNUC__
  5. #pragma GCC diagnostic push
  6. #pragma GCC diagnostic ignored "-Wconversion"
  7. #pragma GCC diagnostic ignored "-Wsign-conversion"
  8. #endif
  9. #ifndef BIT_AT
  10. # define BIT_AT(a, i) \
  11. (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
  12. (1 << ((unsigned int) (i) & 7))))
  13. #endif
  14. #if LLHTTP_STRICT_MODE
  15. # define T(v) 0
  16. #else
  17. # define T(v) v
  18. #endif
  19. static const uint8_t normal_url_char[32] = {
  20. /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
  21. 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
  22. /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
  23. 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
  24. /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
  25. 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
  26. /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
  27. 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
  28. /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
  29. 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
  30. /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
  31. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  32. /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
  33. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  34. /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
  35. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
  36. /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
  37. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  38. /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
  39. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  40. /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
  41. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  42. /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
  43. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  44. /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
  45. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  46. /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
  47. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  48. /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
  49. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
  50. /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
  51. 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
  52. #undef T
  53. enum state
  54. { s_dead = 1 /* important that this is > 0 */
  55. , s_start_req_or_res
  56. , s_res_or_resp_H
  57. , s_start_res
  58. , s_res_H
  59. , s_res_HT
  60. , s_res_HTT
  61. , s_res_HTTP
  62. , s_res_http_major
  63. , s_res_http_dot
  64. , s_res_http_minor
  65. , s_res_http_end
  66. , s_res_first_status_code
  67. , s_res_status_code
  68. , s_res_status_start
  69. , s_res_status
  70. , s_res_line_almost_done
  71. , s_start_req
  72. , s_req_method
  73. , s_req_spaces_before_url
  74. , s_req_schema
  75. , s_req_schema_slash
  76. , s_req_schema_slash_slash
  77. , s_req_server_start
  78. , s_req_server
  79. , s_req_server_with_at
  80. , s_req_path
  81. , s_req_query_string_start
  82. , s_req_query_string
  83. , s_req_fragment_start
  84. , s_req_fragment
  85. , s_req_http_start
  86. , s_req_http_H
  87. , s_req_http_HT
  88. , s_req_http_HTT
  89. , s_req_http_HTTP
  90. , s_req_http_I
  91. , s_req_http_IC
  92. , s_req_http_major
  93. , s_req_http_dot
  94. , s_req_http_minor
  95. , s_req_http_end
  96. , s_req_line_almost_done
  97. , s_header_field_start
  98. , s_header_field
  99. , s_header_value_discard_ws
  100. , s_header_value_discard_ws_almost_done
  101. , s_header_value_discard_lws
  102. , s_header_value_start
  103. , s_header_value
  104. , s_header_value_lws
  105. , s_header_almost_done
  106. , s_chunk_size_start
  107. , s_chunk_size
  108. , s_chunk_parameters
  109. , s_chunk_size_almost_done
  110. , s_headers_almost_done
  111. , s_headers_done
  112. /* Important: 's_headers_done' must be the last 'header' state. All
  113. * states beyond this must be 'body' states. It is used for overflow
  114. * checking. See the PARSING_HEADER() macro.
  115. */
  116. , s_chunk_data
  117. , s_chunk_data_almost_done
  118. , s_chunk_data_done
  119. , s_body_identity
  120. , s_body_identity_eof
  121. , s_message_done
  122. };
  123. enum http_host_state
  124. {
  125. s_http_host_dead = 1
  126. , s_http_userinfo_start
  127. , s_http_userinfo
  128. , s_http_host_start
  129. , s_http_host_v6_start
  130. , s_http_host
  131. , s_http_host_v6
  132. , s_http_host_v6_end
  133. , s_http_host_v6_zone_start
  134. , s_http_host_v6_zone
  135. , s_http_host_port_start
  136. , s_http_host_port
  137. };
  138. /* Macros for character classes; depends on strict-mode */
  139. #define CR '\r'
  140. #define LF '\n'
  141. #define LOWER(c) (unsigned char)(c | 0x20)
  142. #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
  143. #define IS_NUM(c) ((c) >= '0' && (c) <= '9')
  144. #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
  145. #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
  146. #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
  147. (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
  148. (c) == ')')
  149. #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
  150. (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
  151. (c) == '$' || (c) == ',')
  152. #define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
  153. #if LLHTTP_STRICT_MODE
  154. #define TOKEN(c) STRICT_TOKEN(c)
  155. #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
  156. #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
  157. #else
  158. #define TOKEN(c) tokens[(unsigned char)c]
  159. #define IS_URL_CHAR(c) \
  160. (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
  161. #define IS_HOST_CHAR(c) \
  162. (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
  163. #endif
  164. /* Our URL parser.
  165. *
  166. * This is designed to be shared by http_parser_execute() for URL validation,
  167. * hence it has a state transition + byte-for-byte interface. In addition, it
  168. * is meant to be embedded in http_parser_parse_url(), which does the dirty
  169. * work of turning state transitions URL components for its API.
  170. *
  171. * This function should only be invoked with non-space characters. It is
  172. * assumed that the caller cares about (and can detect) the transition between
  173. * URL and non-URL states by looking for these.
  174. */
  175. static enum state
  176. parse_url_char(enum state s, const char ch)
  177. {
  178. if (ch == ' ' || ch == '\r' || ch == '\n') {
  179. return s_dead;
  180. }
  181. #if LLHTTP_STRICT_MODE
  182. if (ch == '\t' || ch == '\f') {
  183. return s_dead;
  184. }
  185. #endif
  186. switch (s) {
  187. case s_req_spaces_before_url:
  188. /* Proxied requests are followed by scheme of an absolute URI (alpha).
  189. * All methods except CONNECT are followed by '/' or '*'.
  190. */
  191. if (ch == '/' || ch == '*') {
  192. return s_req_path;
  193. }
  194. if (IS_ALPHA(ch)) {
  195. return s_req_schema;
  196. }
  197. break;
  198. case s_req_schema:
  199. if (IS_ALPHA(ch)) {
  200. return s;
  201. }
  202. if (ch == ':') {
  203. return s_req_schema_slash;
  204. }
  205. break;
  206. case s_req_schema_slash:
  207. if (ch == '/') {
  208. return s_req_schema_slash_slash;
  209. }
  210. break;
  211. case s_req_schema_slash_slash:
  212. if (ch == '/') {
  213. return s_req_server_start;
  214. }
  215. break;
  216. case s_req_server_with_at:
  217. if (ch == '@') {
  218. return s_dead;
  219. }
  220. /* fall through */
  221. case s_req_server_start:
  222. case s_req_server:
  223. if (ch == '/') {
  224. return s_req_path;
  225. }
  226. if (ch == '?') {
  227. return s_req_query_string_start;
  228. }
  229. if (ch == '@') {
  230. return s_req_server_with_at;
  231. }
  232. if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
  233. return s_req_server;
  234. }
  235. break;
  236. case s_req_path:
  237. if (IS_URL_CHAR(ch)) {
  238. return s;
  239. }
  240. switch (ch) {
  241. case '?':
  242. return s_req_query_string_start;
  243. case '#':
  244. return s_req_fragment_start;
  245. }
  246. break;
  247. case s_req_query_string_start:
  248. case s_req_query_string:
  249. if (IS_URL_CHAR(ch)) {
  250. return s_req_query_string;
  251. }
  252. switch (ch) {
  253. case '?':
  254. /* allow extra '?' in query string */
  255. return s_req_query_string;
  256. case '#':
  257. return s_req_fragment_start;
  258. }
  259. break;
  260. case s_req_fragment_start:
  261. if (IS_URL_CHAR(ch)) {
  262. return s_req_fragment;
  263. }
  264. switch (ch) {
  265. case '?':
  266. return s_req_fragment;
  267. case '#':
  268. return s;
  269. }
  270. break;
  271. case s_req_fragment:
  272. if (IS_URL_CHAR(ch)) {
  273. return s;
  274. }
  275. switch (ch) {
  276. case '?':
  277. case '#':
  278. return s;
  279. }
  280. break;
  281. default:
  282. break;
  283. }
  284. /* We should never fall out of the switch above unless there's an error */
  285. return s_dead;
  286. }
  287. static enum http_host_state
  288. http_parse_host_char(enum http_host_state s, const char ch) {
  289. switch(s) {
  290. case s_http_userinfo:
  291. case s_http_userinfo_start:
  292. if (ch == '@') {
  293. return s_http_host_start;
  294. }
  295. if (IS_USERINFO_CHAR(ch)) {
  296. return s_http_userinfo;
  297. }
  298. break;
  299. case s_http_host_start:
  300. if (ch == '[') {
  301. return s_http_host_v6_start;
  302. }
  303. if (IS_HOST_CHAR(ch)) {
  304. return s_http_host;
  305. }
  306. break;
  307. case s_http_host:
  308. if (IS_HOST_CHAR(ch)) {
  309. return s_http_host;
  310. }
  311. /* fall through */
  312. case s_http_host_v6_end:
  313. if (ch == ':') {
  314. return s_http_host_port_start;
  315. }
  316. break;
  317. case s_http_host_v6:
  318. if (ch == ']') {
  319. return s_http_host_v6_end;
  320. }
  321. /* fall through */
  322. case s_http_host_v6_start:
  323. if (IS_HEX(ch) || ch == ':' || ch == '.') {
  324. return s_http_host_v6;
  325. }
  326. if (s == s_http_host_v6 && ch == '%') {
  327. return s_http_host_v6_zone_start;
  328. }
  329. break;
  330. case s_http_host_v6_zone:
  331. if (ch == ']') {
  332. return s_http_host_v6_end;
  333. }
  334. /* fall through */
  335. case s_http_host_v6_zone_start:
  336. /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
  337. if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
  338. ch == '~') {
  339. return s_http_host_v6_zone;
  340. }
  341. break;
  342. case s_http_host_port:
  343. case s_http_host_port_start:
  344. if (IS_NUM(ch)) {
  345. return s_http_host_port;
  346. }
  347. break;
  348. default:
  349. break;
  350. }
  351. return s_http_host_dead;
  352. }
  353. static int
  354. http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
  355. enum http_host_state s;
  356. const char *p;
  357. size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
  358. assert(u->field_set & (1 << UF_HOST));
  359. u->field_data[UF_HOST].len = 0;
  360. s = found_at ? s_http_userinfo_start : s_http_host_start;
  361. for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
  362. enum http_host_state new_s = http_parse_host_char(s, *p);
  363. if (new_s == s_http_host_dead) {
  364. return 1;
  365. }
  366. switch(new_s) {
  367. case s_http_host:
  368. if (s != s_http_host) {
  369. u->field_data[UF_HOST].off = (uint16_t)(p - buf);
  370. }
  371. u->field_data[UF_HOST].len++;
  372. break;
  373. case s_http_host_v6:
  374. if (s != s_http_host_v6) {
  375. u->field_data[UF_HOST].off = (uint16_t)(p - buf);
  376. }
  377. u->field_data[UF_HOST].len++;
  378. break;
  379. case s_http_host_v6_zone_start:
  380. case s_http_host_v6_zone:
  381. u->field_data[UF_HOST].len++;
  382. break;
  383. case s_http_host_port:
  384. if (s != s_http_host_port) {
  385. u->field_data[UF_PORT].off = (uint16_t)(p - buf);
  386. u->field_data[UF_PORT].len = 0;
  387. u->field_set |= (1 << UF_PORT);
  388. }
  389. u->field_data[UF_PORT].len++;
  390. break;
  391. case s_http_userinfo:
  392. if (s != s_http_userinfo) {
  393. u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
  394. u->field_data[UF_USERINFO].len = 0;
  395. u->field_set |= (1 << UF_USERINFO);
  396. }
  397. u->field_data[UF_USERINFO].len++;
  398. break;
  399. default:
  400. break;
  401. }
  402. s = new_s;
  403. }
  404. /* Make sure we don't end somewhere unexpected */
  405. switch (s) {
  406. case s_http_host_start:
  407. case s_http_host_v6_start:
  408. case s_http_host_v6:
  409. case s_http_host_v6_zone_start:
  410. case s_http_host_v6_zone:
  411. case s_http_host_port_start:
  412. case s_http_userinfo:
  413. case s_http_userinfo_start:
  414. return 1;
  415. default:
  416. break;
  417. }
  418. return 0;
  419. }
  420. void
  421. http_parser_url_init(struct http_parser_url *u) {
  422. memset(u, 0, sizeof(*u));
  423. }
  424. int
  425. http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
  426. struct http_parser_url *u)
  427. {
  428. enum state s;
  429. const char *p;
  430. enum http_parser_url_fields uf, old_uf;
  431. int found_at = 0;
  432. if (buflen == 0) {
  433. return 1;
  434. }
  435. u->port = u->field_set = 0;
  436. s = is_connect ? s_req_server_start : s_req_spaces_before_url;
  437. old_uf = UF_MAX;
  438. for (p = buf; p < buf + buflen; p++) {
  439. s = parse_url_char(s, *p);
  440. /* Figure out the next field that we're operating on */
  441. switch (s) {
  442. case s_dead:
  443. return 1;
  444. /* Skip delimeters */
  445. case s_req_schema_slash:
  446. case s_req_schema_slash_slash:
  447. case s_req_server_start:
  448. case s_req_query_string_start:
  449. case s_req_fragment_start:
  450. continue;
  451. case s_req_schema:
  452. uf = UF_SCHEMA;
  453. break;
  454. case s_req_server_with_at:
  455. found_at = 1;
  456. /* fall through */
  457. case s_req_server:
  458. uf = UF_HOST;
  459. break;
  460. case s_req_path:
  461. uf = UF_PATH;
  462. break;
  463. case s_req_query_string:
  464. uf = UF_QUERY;
  465. break;
  466. case s_req_fragment:
  467. uf = UF_FRAGMENT;
  468. break;
  469. default:
  470. assert(!"Unexpected state");
  471. return 1;
  472. }
  473. /* Nothing's changed; soldier on */
  474. if (uf == old_uf) {
  475. u->field_data[uf].len++;
  476. continue;
  477. }
  478. u->field_data[uf].off = (uint16_t)(p - buf);
  479. u->field_data[uf].len = 1;
  480. u->field_set |= (1 << uf);
  481. old_uf = uf;
  482. }
  483. /* host must be present if there is a schema */
  484. /* parsing http:///toto will fail */
  485. if ((u->field_set & (1 << UF_SCHEMA)) &&
  486. (u->field_set & (1 << UF_HOST)) == 0) {
  487. return 1;
  488. }
  489. if (u->field_set & (1 << UF_HOST)) {
  490. if (http_parse_host(buf, u, found_at) != 0) {
  491. return 1;
  492. }
  493. }
  494. /* CONNECT requests can only contain "hostname:port" */
  495. if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
  496. return 1;
  497. }
  498. if (u->field_set & (1 << UF_PORT)) {
  499. uint16_t off;
  500. uint16_t len;
  501. const char* p;
  502. const char* end;
  503. unsigned long v;
  504. off = u->field_data[UF_PORT].off;
  505. len = u->field_data[UF_PORT].len;
  506. end = buf + off + len;
  507. /* NOTE: The characters are already validated and are in the [0-9] range */
  508. assert((size_t)(off + len) <= buflen && "Port number overflow");
  509. v = 0;
  510. for (p = buf + off; p < end; p++) {
  511. v *= 10;
  512. v += *p - '0';
  513. /* Ports have a max value of 2^16 */
  514. if (v > 0xffff) {
  515. return 1;
  516. }
  517. }
  518. u->port = (uint16_t) v;
  519. }
  520. return 0;
  521. }
  522. #ifdef __GNUC__
  523. #pragma GCC diagnostic pop
  524. #endif