File: | lwan-request.c |
Warning: | line 1749, column 13 1st function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * lwan - web server | |||
3 | * Copyright (c) 2012-2014 L. A. F. Pereira <l@tia.mat.br> | |||
4 | * | |||
5 | * This program is free software; you can redistribute it and/or | |||
6 | * modify it under the terms of the GNU General Public License | |||
7 | * as published by the Free Software Foundation; either version 2 | |||
8 | * of the License, or any later version. | |||
9 | * | |||
10 | * This program is distributed in the hope that it will be useful, | |||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
13 | * GNU General Public License for more details. | |||
14 | * | |||
15 | * You should have received a copy of the GNU General Public License | |||
16 | * along with this program; if not, write to the Free Software | |||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
18 | */ | |||
19 | ||||
20 | #define _GNU_SOURCE | |||
21 | #include <arpa/inet.h> | |||
22 | #include <assert.h> | |||
23 | #include <ctype.h> | |||
24 | #include <errno(*__errno_location ()).h> | |||
25 | #include <fcntl.h> | |||
26 | #include <inttypes.h> | |||
27 | #include <limits.h> | |||
28 | #include <stddef.h> | |||
29 | #include <stdio.h> | |||
30 | #include <stdlib.h> | |||
31 | #include <string.h> | |||
32 | #include <strings.h> | |||
33 | #include <sys/mman.h> | |||
34 | #include <sys/socket.h> | |||
35 | #include <sys/stat.h> | |||
36 | #include <sys/types.h> | |||
37 | #include <sys/vfs.h> | |||
38 | #include <unistd.h> | |||
39 | ||||
40 | #include "lwan-private.h" | |||
41 | ||||
42 | #include "base64.h" | |||
43 | #include "list.h" | |||
44 | #include "lwan-config.h" | |||
45 | #include "lwan-http-authorize.h" | |||
46 | #include "lwan-io-wrappers.h" | |||
47 | #include "sha1.h" | |||
48 | ||||
49 | #define HEADER_VALUE_SEPARATOR_LEN(sizeof(": ") - 1) (sizeof(": ") - 1) | |||
50 | #define HEADER_TERMINATOR_LEN(sizeof("\r\n") - 1) (sizeof("\r\n") - 1) | |||
51 | #define MIN_REQUEST_SIZE(sizeof("GET / HTTP/1.1\r\n\r\n") - 1) (sizeof("GET / HTTP/1.1\r\n\r\n") - 1) | |||
52 | ||||
53 | enum lwan_read_finalizer { | |||
54 | FINALIZER_DONE, | |||
55 | FINALIZER_TRY_AGAIN, | |||
56 | FINALIZER_TIMEOUT, | |||
57 | }; | |||
58 | ||||
59 | struct proxy_header_v2 { | |||
60 | uint8_t sig[12]; | |||
61 | uint8_t cmd_ver; | |||
62 | uint8_t fam; | |||
63 | uint16_t len; | |||
64 | union { | |||
65 | struct { | |||
66 | in_addr_t src_addr; | |||
67 | in_addr_t dst_addr; | |||
68 | uint16_t src_port; | |||
69 | uint16_t dst_port; | |||
70 | } ip4; | |||
71 | struct { | |||
72 | struct in6_addr src_addr; | |||
73 | struct in6_addr dst_addr; | |||
74 | uint16_t src_port; | |||
75 | uint16_t dst_port; | |||
76 | } ip6; | |||
77 | } addr; | |||
78 | }; | |||
79 | ||||
80 | static char *ignore_leading_whitespace(char *buffer) __attribute__((pure)); | |||
81 | ||||
82 | ||||
83 | static bool_Bool | |||
84 | parse_ascii_port(char *port, unsigned short *out) | |||
85 | { | |||
86 | unsigned long parsed; | |||
87 | char *end_ptr; | |||
88 | ||||
89 | errno(*__errno_location ()) = 0; | |||
90 | parsed = strtoul(port, &end_ptr, 10); | |||
91 | ||||
92 | if (UNLIKELY(errno != 0)__builtin_expect((((*__errno_location ()) != 0)), (0))) | |||
93 | return false0; | |||
94 | ||||
95 | if (UNLIKELY(*end_ptr != '\0')__builtin_expect(((*end_ptr != '\0')), (0))) | |||
96 | return false0; | |||
97 | ||||
98 | if (UNLIKELY((unsigned long)(unsigned short)parsed != parsed)__builtin_expect((((unsigned long)(unsigned short)parsed != parsed )), (0))) | |||
99 | return false0; | |||
100 | ||||
101 | *out = htons((unsigned short)parsed); | |||
102 | return true1; | |||
103 | } | |||
104 | ||||
105 | static char * | |||
106 | strsep_char(char *strp, const char *end, char delim) | |||
107 | { | |||
108 | char *ptr; | |||
109 | ||||
110 | if (UNLIKELY(!strp)__builtin_expect(((!strp)), (0))) | |||
111 | return NULL((void*)0); | |||
112 | ||||
113 | if (UNLIKELY(strp > end)__builtin_expect(((strp > end)), (0))) | |||
114 | return NULL((void*)0); | |||
115 | ||||
116 | ptr = strchr(strp, delim); | |||
117 | if (UNLIKELY(!ptr)__builtin_expect(((!ptr)), (0))) | |||
118 | return NULL((void*)0); | |||
119 | ||||
120 | *ptr = '\0'; | |||
121 | return ptr + 1; | |||
122 | } | |||
123 | ||||
124 | static char * | |||
125 | parse_proxy_protocol_v1(struct lwan_request *request, char *buffer) | |||
126 | { | |||
127 | static const size_t line_size = 108; | |||
128 | char *end, *protocol, *src_addr, *dst_addr, *src_port, *dst_port; | |||
129 | unsigned int size; | |||
130 | struct lwan_proxy *const proxy = request->proxy; | |||
131 | ||||
132 | end = memchr(buffer, '\r', line_size); | |||
133 | if (UNLIKELY(!end || end[1] != '\n')__builtin_expect(((!end || end[1] != '\n')), (0))) | |||
134 | return NULL((void*)0); | |||
135 | *end = '\0'; | |||
136 | size = (unsigned int) (end + 2 - buffer); | |||
137 | ||||
138 | protocol = buffer + sizeof("PROXY ") - 1; | |||
139 | src_addr = strsep_char(protocol, end, ' '); | |||
140 | dst_addr = strsep_char(src_addr, end, ' '); | |||
141 | src_port = strsep_char(dst_addr, end, ' '); | |||
142 | dst_port = strsep_char(src_port, end, ' '); | |||
143 | ||||
144 | if (UNLIKELY(!dst_port)__builtin_expect(((!dst_port)), (0))) | |||
145 | return NULL((void*)0); | |||
146 | ||||
147 | STRING_SWITCH(protocol)switch (string_as_uint32(protocol)) { | |||
148 | case STR4_INT('T', 'C', 'P', '4')((uint32_t)(('T') | ('C') << 8 | ('P') << 16 | ('4' ) << 24)): { | |||
149 | struct sockaddr_in *from = &proxy->from.ipv4; | |||
150 | struct sockaddr_in *to = &proxy->to.ipv4; | |||
151 | ||||
152 | from->sin_family = to->sin_family = AF_INET2; | |||
153 | ||||
154 | if (UNLIKELY(inet_pton(AF_INET, src_addr, &from->sin_addr) <= 0)__builtin_expect(((inet_pton(2, src_addr, &from->sin_addr ) <= 0)), (0))) | |||
155 | return NULL((void*)0); | |||
156 | if (UNLIKELY(inet_pton(AF_INET, dst_addr, &to->sin_addr) <= 0)__builtin_expect(((inet_pton(2, dst_addr, &to->sin_addr ) <= 0)), (0))) | |||
157 | return NULL((void*)0); | |||
158 | if (UNLIKELY(!parse_ascii_port(src_port, &from->sin_port))__builtin_expect(((!parse_ascii_port(src_port, &from-> sin_port))), (0))) | |||
159 | return NULL((void*)0); | |||
160 | if (UNLIKELY(!parse_ascii_port(dst_port, &to->sin_port))__builtin_expect(((!parse_ascii_port(dst_port, &to->sin_port ))), (0))) | |||
161 | return NULL((void*)0); | |||
162 | ||||
163 | break; | |||
164 | } | |||
165 | case STR4_INT('T', 'C', 'P', '6')((uint32_t)(('T') | ('C') << 8 | ('P') << 16 | ('6' ) << 24)): { | |||
166 | struct sockaddr_in6 *from = &proxy->from.ipv6; | |||
167 | struct sockaddr_in6 *to = &proxy->to.ipv6; | |||
168 | ||||
169 | from->sin6_family = to->sin6_family = AF_INET610; | |||
170 | ||||
171 | if (UNLIKELY(inet_pton(AF_INET6, src_addr, &from->sin6_addr) <= 0)__builtin_expect(((inet_pton(10, src_addr, &from->sin6_addr ) <= 0)), (0))) | |||
172 | return NULL((void*)0); | |||
173 | if (UNLIKELY(inet_pton(AF_INET6, dst_addr, &to->sin6_addr) <= 0)__builtin_expect(((inet_pton(10, dst_addr, &to->sin6_addr ) <= 0)), (0))) | |||
174 | return NULL((void*)0); | |||
175 | if (UNLIKELY(!parse_ascii_port(src_port, &from->sin6_port))__builtin_expect(((!parse_ascii_port(src_port, &from-> sin6_port))), (0))) | |||
176 | return NULL((void*)0); | |||
177 | if (UNLIKELY(!parse_ascii_port(dst_port, &to->sin6_port))__builtin_expect(((!parse_ascii_port(dst_port, &to->sin6_port ))), (0))) | |||
178 | return NULL((void*)0); | |||
179 | ||||
180 | break; | |||
181 | } | |||
182 | default: | |||
183 | return NULL((void*)0); | |||
184 | } | |||
185 | ||||
186 | request->flags |= REQUEST_PROXIED; | |||
187 | return buffer + size; | |||
188 | } | |||
189 | ||||
190 | static char *parse_proxy_protocol_v2(struct lwan_request *request, char *buffer) | |||
191 | { | |||
192 | struct proxy_header_v2 *hdr = (struct proxy_header_v2 *)buffer; | |||
193 | struct lwan_request_parser_helper *helper = request->helper; | |||
194 | const unsigned int proto_signature_length = 16; | |||
195 | unsigned int size; | |||
196 | struct lwan_proxy *const proxy = request->proxy; | |||
197 | ||||
198 | enum { LOCAL = 0x20, PROXY = 0x21, TCP4 = 0x11, TCP6 = 0x21 }; | |||
199 | ||||
200 | size = proto_signature_length + (unsigned int)ntohs(hdr->len); | |||
201 | if (UNLIKELY(size > (unsigned int)sizeof(*hdr))__builtin_expect(((size > (unsigned int)sizeof(*hdr))), (0 ))) | |||
202 | return NULL((void*)0); | |||
203 | if (UNLIKELY(size >= helper->buffer->len)__builtin_expect(((size >= helper->buffer->len)), (0 ))) | |||
204 | return NULL((void*)0); | |||
205 | ||||
206 | if (LIKELY(hdr->cmd_ver == PROXY)__builtin_expect((!!(hdr->cmd_ver == PROXY)), (1))) { | |||
207 | if (hdr->fam == TCP4) { | |||
208 | struct sockaddr_in *from = &proxy->from.ipv4; | |||
209 | struct sockaddr_in *to = &proxy->to.ipv4; | |||
210 | ||||
211 | to->sin_family = from->sin_family = AF_INET2; | |||
212 | ||||
213 | from->sin_addr.s_addr = hdr->addr.ip4.src_addr; | |||
214 | from->sin_port = hdr->addr.ip4.src_port; | |||
215 | ||||
216 | to->sin_addr.s_addr = hdr->addr.ip4.dst_addr; | |||
217 | to->sin_port = hdr->addr.ip4.dst_port; | |||
218 | } else if (hdr->fam == TCP6) { | |||
219 | struct sockaddr_in6 *from = &proxy->from.ipv6; | |||
220 | struct sockaddr_in6 *to = &proxy->to.ipv6; | |||
221 | ||||
222 | from->sin6_family = to->sin6_family = AF_INET610; | |||
223 | ||||
224 | from->sin6_addr = hdr->addr.ip6.src_addr; | |||
225 | from->sin6_port = hdr->addr.ip6.src_port; | |||
226 | ||||
227 | to->sin6_addr = hdr->addr.ip6.dst_addr; | |||
228 | to->sin6_port = hdr->addr.ip6.dst_port; | |||
229 | } else { | |||
230 | return NULL((void*)0); | |||
231 | } | |||
232 | } else if (hdr->cmd_ver == LOCAL) { | |||
233 | struct sockaddr_in *from = &proxy->from.ipv4; | |||
234 | struct sockaddr_in *to = &proxy->to.ipv4; | |||
235 | ||||
236 | from->sin_family = to->sin_family = AF_UNSPEC0; | |||
237 | } else { | |||
238 | return NULL((void*)0); | |||
239 | } | |||
240 | ||||
241 | request->flags |= REQUEST_PROXIED; | |||
242 | return buffer + size; | |||
243 | } | |||
244 | ||||
245 | #if !defined(LWAN_HAVE_BUILTIN_EXPECT_PROBABILITY) | |||
246 | #define __builtin_expect_with_probability(value1, value2, probability) \ | |||
247 | __builtin_expect(value1, value2) | |||
248 | #endif | |||
249 | ||||
250 | static ALWAYS_INLINEinline __attribute__((always_inline)) char *identify_http_method(struct lwan_request *request, | |||
251 | char *buffer) | |||
252 | { | |||
253 | const uint32_t first_four = string_as_uint32(buffer); | |||
254 | ||||
255 | #define GENERATE_IF(upper, lower, mask, constant, probability) \ | |||
256 | if (__builtin_expect_with_probability(first_four == (constant), 1, \ | |||
257 | probability)) { \ | |||
258 | request->flags |= (mask); \ | |||
259 | return buffer + sizeof(#upper); \ | |||
260 | } | |||
261 | ||||
262 | FOR_EACH_REQUEST_METHOD(GENERATE_IF)GENERATE_IF(GET, get, (1 << 0), (((uint32_t)(('G') | ('E' ) << 8 | ('T') << 16 | (' ') << 24))), 0.6) GENERATE_IF(POST, post, (1 << 3 | 1 << 1 | 1 << 0), (((uint32_t)(('P') | ('O') << 8 | ('S') << 16 | ('T') << 24))), 0.2) GENERATE_IF(HEAD, head, (1 << 1), (((uint32_t)(('H') | ('E') << 8 | ('A') << 16 | ('D') << 24))), 0.2) GENERATE_IF(OPTIONS, options, ( 1 << 2), (((uint32_t)(('O') | ('P') << 8 | ('T') << 16 | ('I') << 24))), 0.1) GENERATE_IF(DELETE, delete, ( 1 << 1 | 1 << 2), (((uint32_t)(('D') | ('E') << 8 | ('L') << 16 | ('E') << 24))), 0.1) GENERATE_IF (PUT, put, (1 << 3 | 1 << 2 | 1 << 0), (((uint32_t )(('P') | ('U') << 8 | ('T') << 16 | (' ') << 24))), 0.1) | |||
263 | ||||
264 | #undef GENERATE_IF | |||
265 | ||||
266 | return NULL((void*)0); | |||
267 | } | |||
268 | ||||
269 | __attribute__((nonnull(1))) static ssize_t url_decode(char *str) | |||
270 | { | |||
271 | static const unsigned char tbl1[256] = { | |||
272 | [0 ... 255] = 255, ['0'] = 0 << 4, ['1'] = 1 << 4, ['2'] = 2 << 4, | |||
273 | ['3'] = 3 << 4, ['4'] = 4 << 4, ['5'] = 5 << 4, ['6'] = 6 << 4, | |||
274 | ['7'] = 7 << 4, ['8'] = 8 << 4, ['9'] = 9 << 4, ['a'] = 10 << 4, | |||
275 | ['b'] = 11 << 4, ['c'] = 12 << 4, ['d'] = 13 << 4, ['e'] = 14 << 4, | |||
276 | ['f'] = 15 << 4, ['A'] = 10 << 4, ['B'] = 11 << 4, ['C'] = 12 << 4, | |||
277 | ['D'] = 13 << 4, ['E'] = 14 << 4, ['F'] = 15 << 4, | |||
278 | }; | |||
279 | static const char tbl2[256] = { | |||
280 | [0 ... 255] = -1, ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, | |||
281 | ['4'] = 4, ['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, | |||
282 | ['9'] = 9, ['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13, | |||
283 | ['e'] = 14, ['f'] = 15, ['A'] = 10, ['B'] = 11, ['C'] = 12, | |||
284 | ['D'] = 13, ['E'] = 14, ['F'] = 15, | |||
285 | }; | |||
286 | const char *inptr = str; | |||
287 | char *outptr = str; | |||
288 | ||||
289 | for (char *p = strpbrk(inptr, "+%"); p; p = strpbrk(inptr, "+%")) { | |||
290 | const ptrdiff_t diff = p - inptr; | |||
291 | if (diff) | |||
292 | outptr = stpncpy(outptr, inptr, (size_t)diff); | |||
293 | ||||
294 | if (*p == '+') { | |||
295 | *outptr++ = ' '; | |||
296 | inptr = p + 1; | |||
297 | continue; | |||
298 | } | |||
299 | ||||
300 | const char first = (char)tbl1[(unsigned char)p[1]]; | |||
301 | const char second = tbl2[(unsigned char)p[2]]; | |||
302 | const char decoded = first | second; | |||
303 | if (UNLIKELY(decoded <= 0)__builtin_expect(((decoded <= 0)), (0))) { | |||
304 | /* This shouldn't happen in normal circumstances, but if %00 is | |||
305 | * found in the encoded string, bail here. */ | |||
306 | if (decoded == '\0') | |||
307 | return -1; | |||
308 | ||||
309 | /* OR-ing both lookups will yield a negative number if either | |||
310 | * encoded character is not a valid hex digit; check it here so | |||
311 | * that other valid-but-negative bytes (e.g. 0xff) are still | |||
312 | * written to outptr. */ | |||
313 | if (first == -1) { | |||
314 | /* tbl1 is shifted so a valid nibble might be negative; | |||
315 | * check for all the bits here instead. */ | |||
316 | return -1; | |||
317 | } | |||
318 | if (second < 0) { | |||
319 | /* tbl2 isn't shifted so we can check for the sign bit only. */ | |||
320 | return -1; | |||
321 | } | |||
322 | } | |||
323 | ||||
324 | *outptr++ = decoded; | |||
325 | inptr = p + 3; | |||
326 | } | |||
327 | ||||
328 | if (inptr > outptr) { | |||
329 | outptr = stpcpy(outptr, inptr); | |||
330 | return (ssize_t)(outptr - str); | |||
331 | } | |||
332 | ||||
333 | return (ssize_t)strlen(str); | |||
334 | } | |||
335 | ||||
336 | static int key_value_compare(const void *a, const void *b) | |||
337 | { | |||
338 | return strcmp(((const struct lwan_key_value *)a)->key, | |||
339 | ((const struct lwan_key_value *)b)->key); | |||
340 | } | |||
341 | ||||
342 | static void | |||
343 | reset_key_value_array(void *data) | |||
344 | { | |||
345 | struct lwan_key_value_array *array = data; | |||
346 | ||||
347 | lwan_key_value_array_reset(array); | |||
348 | } | |||
349 | ||||
350 | static void parse_key_values(struct lwan_request *request, | |||
351 | struct lwan_value *helper_value, | |||
352 | struct lwan_key_value_array *array, | |||
353 | ssize_t (*decode_value)(char *value), | |||
354 | const char separator) | |||
355 | { | |||
356 | struct lwan_key_value *kv; | |||
357 | char *ptr = helper_value->value; | |||
358 | const char *end = helper_value->value + helper_value->len; | |||
359 | coro_deferred reset_defer; | |||
360 | ||||
361 | if (!helper_value->len) | |||
362 | return; | |||
363 | ||||
364 | lwan_key_value_array_init(array); | |||
365 | reset_defer = coro_defer(request->conn->coro, reset_key_value_array, array); | |||
366 | ||||
367 | do { | |||
368 | char *key, *value; | |||
369 | ||||
370 | while (*ptr == ' ' || *ptr == separator) | |||
371 | ptr++; | |||
372 | if (UNLIKELY(*ptr == '\0')__builtin_expect(((*ptr == '\0')), (0))) | |||
373 | break; | |||
374 | ||||
375 | key = ptr; | |||
376 | ptr = strsep_char(key, end, separator); | |||
377 | ||||
378 | value = strsep_char(key, end, '='); | |||
379 | if (UNLIKELY(!value)__builtin_expect(((!value)), (0))) { | |||
380 | value = ""; | |||
381 | } else if (UNLIKELY(decode_value(value) < 0)__builtin_expect(((decode_value(value) < 0)), (0))) { | |||
382 | /* Disallow values that failed decoding, but allow empty values */ | |||
383 | goto error; | |||
384 | } | |||
385 | ||||
386 | if (UNLIKELY(decode_value(key) <= 0)__builtin_expect(((decode_value(key) <= 0)), (0))) { | |||
387 | /* Disallow keys that failed decoding, or empty keys */ | |||
388 | goto error; | |||
389 | } | |||
390 | ||||
391 | kv = lwan_key_value_array_append(array); | |||
392 | if (UNLIKELY(!kv)__builtin_expect(((!kv)), (0))) | |||
393 | goto error; | |||
394 | ||||
395 | kv->key = key; | |||
396 | kv->value = value; | |||
397 | } while (ptr); | |||
398 | ||||
399 | lwan_key_value_array_sort(array, key_value_compare); | |||
400 | ||||
401 | return; | |||
402 | ||||
403 | error: | |||
404 | coro_defer_fire_and_disarm(request->conn->coro, reset_defer); | |||
405 | } | |||
406 | ||||
407 | static ssize_t | |||
408 | identity_decode(char *input __attribute__((unused))) | |||
409 | { | |||
410 | return 1; | |||
411 | } | |||
412 | ||||
413 | static void parse_cookies(struct lwan_request *request) | |||
414 | { | |||
415 | const char *cookies = lwan_request_get_header(request, "Cookie"); | |||
416 | ||||
417 | if (!cookies) | |||
418 | return; | |||
419 | ||||
420 | struct lwan_value header = {.value = (char *)cookies, | |||
421 | .len = strlen(cookies)}; | |||
422 | parse_key_values(request, &header, &request->helper->cookies, | |||
423 | identity_decode, ';'); | |||
424 | } | |||
425 | ||||
426 | static void parse_query_string(struct lwan_request *request) | |||
427 | { | |||
428 | struct lwan_request_parser_helper *helper = request->helper; | |||
429 | ||||
430 | parse_key_values(request, &helper->query_string, &helper->query_params, | |||
431 | url_decode, '&'); | |||
432 | } | |||
433 | ||||
434 | static void parse_form_data(struct lwan_request *request) | |||
435 | { | |||
436 | struct lwan_request_parser_helper *helper = request->helper; | |||
437 | static const char content_type[] = "application/x-www-form-urlencoded"; | |||
438 | ||||
439 | if (helper->content_type.len < sizeof(content_type) - 1) | |||
440 | return; | |||
441 | if (UNLIKELY(strncmp(helper->content_type.value, content_type,__builtin_expect(((strncmp(helper->content_type.value, content_type , sizeof(content_type) - 1))), (0)) | |||
442 | sizeof(content_type) - 1))__builtin_expect(((strncmp(helper->content_type.value, content_type , sizeof(content_type) - 1))), (0))) | |||
443 | return; | |||
444 | ||||
445 | parse_key_values(request, &helper->body_data, &helper->post_params, | |||
446 | url_decode, '&'); | |||
447 | } | |||
448 | ||||
449 | static void find_query_string(struct lwan_request *request, const char *space) | |||
450 | { | |||
451 | struct lwan_request_parser_helper *helper = request->helper; | |||
452 | ||||
453 | char *query_string = memchr(request->url.value, '?', request->url.len); | |||
454 | if (query_string) { | |||
455 | *query_string = '\0'; | |||
456 | helper->query_string.value = query_string + 1; | |||
457 | helper->query_string.len = (size_t)(space - query_string - 1); | |||
458 | request->url.len -= helper->query_string.len + 1; | |||
459 | request->flags |= REQUEST_HAS_QUERY_STRING; | |||
460 | } | |||
461 | } | |||
462 | ||||
463 | static char * | |||
464 | identify_http_path(struct lwan_request *request, char *buffer) | |||
465 | { | |||
466 | struct lwan_request_parser_helper *helper = request->helper; | |||
467 | static const size_t minimal_request_line_len = sizeof("/ HTTP/1.0") - 1; | |||
468 | char *space, *end_of_line; | |||
469 | ptrdiff_t end_len; | |||
470 | ||||
471 | if (UNLIKELY(*buffer != '/')__builtin_expect(((*buffer != '/')), (0))) | |||
472 | return NULL((void*)0); | |||
473 | ||||
474 | end_len = buffer - helper->buffer->value; | |||
475 | if (UNLIKELY((size_t)end_len >= helper->buffer->len)__builtin_expect((((size_t)end_len >= helper->buffer-> len)), (0))) | |||
476 | return NULL((void*)0); | |||
477 | ||||
478 | end_of_line = memchr(buffer, '\r', helper->buffer->len - (size_t)end_len); | |||
479 | if (UNLIKELY(!end_of_line)__builtin_expect(((!end_of_line)), (0))) | |||
480 | return NULL((void*)0); | |||
481 | if (UNLIKELY((size_t)(end_of_line - buffer) < minimal_request_line_len)__builtin_expect((((size_t)(end_of_line - buffer) < minimal_request_line_len )), (0))) | |||
482 | return NULL((void*)0); | |||
483 | *end_of_line = '\0'; | |||
484 | ||||
485 | space = end_of_line - sizeof("HTTP/X.X"); | |||
486 | ||||
487 | request->url.value = buffer; | |||
488 | request->url.len = (size_t)(space - buffer); | |||
489 | find_query_string(request, space); | |||
490 | request->original_url = request->url; | |||
491 | ||||
492 | *space++ = '\0'; | |||
493 | ||||
494 | STRING_SWITCH_LARGE(space)switch (string_as_uint64(space)) { | |||
495 | case STR8_INT('H','T','T','P','/','1','.','0')((uint64_t)((uint32_t)(('H') | ('T') << 8 | ('T') << 16 | ('P') << 24)) | (uint64_t)((uint32_t)(('/') | ('1' ) << 8 | ('.') << 16 | ('0') << 24)) << 32): | |||
496 | request->flags |= REQUEST_IS_HTTP_1_0; | |||
497 | break; | |||
498 | case STR8_INT('H','T','T','P','/','1','.','1')((uint64_t)((uint32_t)(('H') | ('T') << 8 | ('T') << 16 | ('P') << 24)) | (uint64_t)((uint32_t)(('/') | ('1' ) << 8 | ('.') << 16 | ('1') << 24)) << 32): | |||
499 | break; | |||
500 | default: | |||
501 | return NULL((void*)0); | |||
502 | } | |||
503 | ||||
504 | return end_of_line + 1; | |||
505 | } | |||
506 | ||||
507 | __attribute__((noinline)) static void set_header_value( | |||
508 | struct lwan_value *header, char *end, char *p, size_t header_len) | |||
509 | { | |||
510 | p += header_len; | |||
511 | ||||
512 | if (LIKELY(string_as_uint16(p) == STR2_INT(':', ' '))__builtin_expect((!!(string_as_uint16(p) == ((uint16_t)((':') | (' ') << 8)))), (1))) { | |||
513 | *end = '\0'; | |||
514 | char *value = p + sizeof(": ") - 1; | |||
515 | ||||
516 | header->value = value; | |||
517 | header->len = (size_t)(end - value); | |||
518 | } | |||
519 | } | |||
520 | ||||
521 | #define HEADER_LENGTH(hdr) \ | |||
522 | ({ \ | |||
523 | if (UNLIKELY(end - sizeof(hdr) + 1 < p)__builtin_expect(((end - sizeof(hdr) + 1 < p)), (0))) \ | |||
524 | continue; \ | |||
525 | sizeof(hdr) - 1; \ | |||
526 | }) | |||
527 | ||||
528 | #define SET_HEADER_VALUE(dest, hdr) \ | |||
529 | do { \ | |||
530 | const size_t header_len = HEADER_LENGTH(hdr); \ | |||
531 | set_header_value(&(helper->dest), end, p, header_len); \ | |||
532 | } while (0) | |||
533 | ||||
534 | static ALWAYS_INLINEinline __attribute__((always_inline)) ssize_t find_headers(char **header_start, | |||
535 | struct lwan_value *request_buffer, | |||
536 | char **next_request) | |||
537 | { | |||
538 | char *buffer = request_buffer->value; | |||
539 | char *buffer_end = buffer + request_buffer->len; | |||
540 | ssize_t n_headers = 0; | |||
541 | char *next_header; | |||
542 | ||||
543 | for (char *next_chr = buffer + 1;;) { | |||
544 | next_header = memchr(next_chr, '\r', (size_t)(buffer_end - next_chr)); | |||
545 | ||||
546 | if (UNLIKELY(!next_header)__builtin_expect(((!next_header)), (0))) | |||
547 | return -1; | |||
548 | ||||
549 | if (next_chr == next_header) { | |||
550 | if (buffer_end - next_chr >= (ptrdiff_t)HEADER_TERMINATOR_LEN(sizeof("\r\n") - 1)) { | |||
551 | STRING_SWITCH_SMALL (next_header)switch (string_as_uint16(next_header)) { | |||
552 | case STR2_INT('\r', '\n')((uint16_t)(('\r') | ('\n') << 8)): | |||
553 | *next_request = next_header + HEADER_TERMINATOR_LEN(sizeof("\r\n") - 1); | |||
554 | } | |||
555 | } | |||
556 | goto out; | |||
557 | } | |||
558 | ||||
559 | /* Is there at least a space for a minimal (H)eader and a (V)alue? */ | |||
560 | if (LIKELY(next_header - next_chr >= (ptrdiff_t)(sizeof("H: V") - 1))__builtin_expect((!!(next_header - next_chr >= (ptrdiff_t) (sizeof("H: V") - 1))), (1))) { | |||
561 | header_start[n_headers++] = next_chr; | |||
562 | ||||
563 | if (UNLIKELY(n_headers >= N_HEADER_START - 1)__builtin_expect(((n_headers >= 64 - 1)), (0))) | |||
564 | return -1; | |||
565 | } else { | |||
566 | /* Better to abort early if there's no space. */ | |||
567 | return -1; | |||
568 | } | |||
569 | ||||
570 | next_chr = next_header + HEADER_TERMINATOR_LEN(sizeof("\r\n") - 1); | |||
571 | if (UNLIKELY(next_chr >= buffer_end)__builtin_expect(((next_chr >= buffer_end)), (0))) | |||
572 | return -1; | |||
573 | } | |||
574 | ||||
575 | out: | |||
576 | header_start[n_headers] = next_header; | |||
577 | return n_headers; | |||
578 | } | |||
579 | ||||
580 | static bool_Bool parse_headers(struct lwan_request_parser_helper *helper, | |||
581 | char *buffer) | |||
582 | { | |||
583 | char **header_start = helper->header_start; | |||
584 | ssize_t n_headers = 0; | |||
585 | ||||
586 | /* FIXME: is there a better way to do this? */ | |||
587 | struct lwan_value header_start_buffer = { | |||
588 | .value = buffer, | |||
589 | .len = helper->buffer->len - (size_t)(buffer - helper->buffer->value) | |||
590 | }; | |||
591 | n_headers = find_headers(header_start, &header_start_buffer, | |||
592 | &helper->next_request); | |||
593 | if (UNLIKELY(n_headers < 0)__builtin_expect(((n_headers < 0)), (0))) | |||
594 | return false0; | |||
595 | ||||
596 | for (ssize_t i = 0; i < n_headers; i++) { | |||
597 | char *p = header_start[i]; | |||
598 | char *end = header_start[i + 1] - HEADER_TERMINATOR_LEN(sizeof("\r\n") - 1); | |||
599 | ||||
600 | STRING_SWITCH_L (p)switch (((string_as_uint32(p)) | (uint32_t)0x20202020)) { | |||
601 | case STR4_INT_L('A', 'c', 'c', 'e')((((uint32_t)(('A') | ('c') << 8 | ('c') << 16 | ( 'e') << 24))) | (uint32_t)0x20202020): | |||
602 | p += HEADER_LENGTH("Accept"); | |||
603 | ||||
604 | STRING_SWITCH_L (p)switch (((string_as_uint32(p)) | (uint32_t)0x20202020)) { | |||
605 | case STR4_INT_L('-', 'E', 'n', 'c')((((uint32_t)(('-') | ('E') << 8 | ('n') << 16 | ( 'c') << 24))) | (uint32_t)0x20202020): | |||
606 | SET_HEADER_VALUE(accept_encoding, "-Encoding"); | |||
607 | break; | |||
608 | } | |||
609 | break; | |||
610 | case STR4_INT_L('C', 'o', 'n', 'n')((((uint32_t)(('C') | ('o') << 8 | ('n') << 16 | ( 'n') << 24))) | (uint32_t)0x20202020): | |||
611 | SET_HEADER_VALUE(connection, "Connection"); | |||
612 | break; | |||
613 | case STR4_INT_L('C', 'o', 'n', 't')((((uint32_t)(('C') | ('o') << 8 | ('n') << 16 | ( 't') << 24))) | (uint32_t)0x20202020): | |||
614 | p += HEADER_LENGTH("Content"); | |||
615 | ||||
616 | STRING_SWITCH_L (p)switch (((string_as_uint32(p)) | (uint32_t)0x20202020)) { | |||
617 | case STR4_INT_L('-', 'T', 'y', 'p')((((uint32_t)(('-') | ('T') << 8 | ('y') << 16 | ( 'p') << 24))) | (uint32_t)0x20202020): | |||
618 | SET_HEADER_VALUE(content_type, "-Type"); | |||
619 | break; | |||
620 | case STR4_INT_L('-', 'L', 'e', 'n')((((uint32_t)(('-') | ('L') << 8 | ('e') << 16 | ( 'n') << 24))) | (uint32_t)0x20202020): | |||
621 | SET_HEADER_VALUE(content_length, "-Length"); | |||
622 | break; | |||
623 | } | |||
624 | break; | |||
625 | case STR4_INT_L('I', 'f', '-', 'M')((((uint32_t)(('I') | ('f') << 8 | ('-') << 16 | ( 'M') << 24))) | (uint32_t)0x20202020): | |||
626 | SET_HEADER_VALUE(if_modified_since.raw, "If-Modified-Since"); | |||
627 | break; | |||
628 | case STR4_INT_L('H', 'o', 's', 't')((((uint32_t)(('H') | ('o') << 8 | ('s') << 16 | ( 't') << 24))) | (uint32_t)0x20202020): | |||
629 | SET_HEADER_VALUE(host, "Host"); | |||
630 | break; | |||
631 | case STR4_INT_L('R', 'a', 'n', 'g')((((uint32_t)(('R') | ('a') << 8 | ('n') << 16 | ( 'g') << 24))) | (uint32_t)0x20202020): | |||
632 | SET_HEADER_VALUE(range.raw, "Range"); | |||
633 | break; | |||
634 | } | |||
635 | } | |||
636 | ||||
637 | helper->n_header_start = (size_t)n_headers; | |||
638 | return true1; | |||
639 | } | |||
640 | #undef HEADER_LENGTH | |||
641 | #undef SET_HEADER_VALUE | |||
642 | ||||
643 | ssize_t lwan_find_headers(char **header_start, struct lwan_value *buffer, | |||
644 | char **next_request) | |||
645 | { | |||
646 | return find_headers(header_start, buffer, next_request); | |||
647 | } | |||
648 | ||||
649 | static void parse_if_modified_since(struct lwan_request_parser_helper *helper) | |||
650 | { | |||
651 | static const size_t header_len = | |||
652 | sizeof("Wed, 17 Apr 2019 13:59:27 GMT") - 1; | |||
653 | time_t parsed; | |||
654 | ||||
655 | if (UNLIKELY(helper->if_modified_since.raw.len != header_len)__builtin_expect(((helper->if_modified_since.raw.len != header_len )), (0))) | |||
656 | return; | |||
657 | ||||
658 | if (UNLIKELY(lwan_parse_rfc_time(helper->if_modified_since.raw.value,__builtin_expect(((lwan_parse_rfc_time(helper->if_modified_since .raw.value, &parsed) < 0)), (0)) | |||
659 | &parsed) < 0)__builtin_expect(((lwan_parse_rfc_time(helper->if_modified_since .raw.value, &parsed) < 0)), (0))) | |||
660 | return; | |||
661 | ||||
662 | helper->if_modified_since.parsed = parsed; | |||
663 | } | |||
664 | ||||
665 | static bool_Bool | |||
666 | parse_off_without_sign(const char *ptr, char **end, off_t *off) | |||
667 | { | |||
668 | unsigned long long val; | |||
669 | ||||
670 | static_assert_Static_assert(sizeof(val) >= sizeof(off_t), | |||
671 | "off_t fits in a long long"); | |||
672 | ||||
673 | errno(*__errno_location ()) = 0; | |||
674 | ||||
675 | val = strtoull(ptr, end, 10); | |||
676 | if (UNLIKELY(val == 0 && *end == ptr)__builtin_expect(((val == 0 && *end == ptr)), (0))) | |||
677 | return false0; | |||
678 | if (UNLIKELY(errno != 0)__builtin_expect((((*__errno_location ()) != 0)), (0))) | |||
679 | return false0; | |||
680 | if (UNLIKELY(val > OFF_MAX)__builtin_expect(((val > 9223372036854775807LL)), (0))) | |||
681 | return false0; | |||
682 | ||||
683 | *off = (off_t)val; | |||
684 | return true1; | |||
685 | } | |||
686 | ||||
687 | static void | |||
688 | parse_range(struct lwan_request_parser_helper *helper) | |||
689 | { | |||
690 | if (UNLIKELY(helper->range.raw.len <= (sizeof("bytes=") - 1))__builtin_expect(((helper->range.raw.len <= (sizeof("bytes=" ) - 1))), (0))) | |||
691 | return; | |||
692 | ||||
693 | char *range = helper->range.raw.value; | |||
694 | if (UNLIKELY(strncmp(range, "bytes=", sizeof("bytes=") - 1))__builtin_expect(((strncmp(range, "bytes=", sizeof("bytes=") - 1))), (0))) | |||
695 | return; | |||
696 | ||||
697 | range += sizeof("bytes=") - 1; | |||
698 | ||||
699 | off_t from, to; | |||
700 | char *end; | |||
701 | ||||
702 | if (*range == '-') { | |||
703 | from = 0; | |||
704 | ||||
705 | if (!parse_off_without_sign(range + 1, &end, &to)) | |||
706 | goto invalid_range; | |||
707 | if (*end != '\0') | |||
708 | goto invalid_range; | |||
709 | } else if (lwan_char_isdigit(*range)) { | |||
710 | if (!parse_off_without_sign(range, &end, &from)) | |||
711 | goto invalid_range; | |||
712 | if (*end != '-') | |||
713 | goto invalid_range; | |||
714 | ||||
715 | range = end + 1; | |||
716 | if (*range == '\0') { | |||
717 | to = -1; | |||
718 | } else { | |||
719 | if (!parse_off_without_sign(range, &end, &to)) | |||
720 | goto invalid_range; | |||
721 | if (*end != '\0') | |||
722 | goto invalid_range; | |||
723 | } | |||
724 | } else { | |||
725 | invalid_range: | |||
726 | to = from = -1; | |||
727 | } | |||
728 | ||||
729 | helper->range.from = from; | |||
730 | helper->range.to = to; | |||
731 | } | |||
732 | ||||
733 | static void | |||
734 | parse_accept_encoding(struct lwan_request *request) | |||
735 | { | |||
736 | struct lwan_request_parser_helper *helper = request->helper; | |||
737 | ||||
738 | if (!helper->accept_encoding.len) | |||
739 | return; | |||
740 | ||||
741 | for (const char *p = helper->accept_encoding.value; *p; p++) { | |||
742 | STRING_SWITCH(p)switch (string_as_uint32(p)) { | |||
743 | case STR4_INT('d','e','f','l')((uint32_t)(('d') | ('e') << 8 | ('f') << 16 | ('l' ) << 24)): | |||
744 | case STR4_INT(' ','d','e','f')((uint32_t)((' ') | ('d') << 8 | ('e') << 16 | ('f' ) << 24)): | |||
745 | request->flags |= REQUEST_ACCEPT_DEFLATE; | |||
746 | break; | |||
747 | case STR4_INT('g','z','i','p')((uint32_t)(('g') | ('z') << 8 | ('i') << 16 | ('p' ) << 24)): | |||
748 | case STR4_INT(' ','g','z','i')((uint32_t)((' ') | ('g') << 8 | ('z') << 16 | ('i' ) << 24)): | |||
749 | request->flags |= REQUEST_ACCEPT_GZIP; | |||
750 | break; | |||
751 | #if defined(LWAN_HAVE_ZSTD) | |||
752 | case STR4_INT('z','s','t','d')((uint32_t)(('z') | ('s') << 8 | ('t') << 16 | ('d' ) << 24)): | |||
753 | case STR4_INT(' ','z','s','t')((uint32_t)((' ') | ('z') << 8 | ('s') << 16 | ('t' ) << 24)): | |||
754 | request->flags |= REQUEST_ACCEPT_ZSTD; | |||
755 | break; | |||
756 | #endif | |||
757 | #if defined(LWAN_HAVE_BROTLI) | |||
758 | default: | |||
759 | while (lwan_char_isspace(*p)) | |||
760 | p++; | |||
761 | ||||
762 | STRING_SWITCH_SMALL(p)switch (string_as_uint16(p)) { | |||
763 | case STR2_INT('b', 'r')((uint16_t)(('b') | ('r') << 8)): | |||
764 | request->flags |= REQUEST_ACCEPT_BROTLI; | |||
765 | break; | |||
766 | } | |||
767 | #endif | |||
768 | } | |||
769 | ||||
770 | if (!(p = strchr(p, ','))) | |||
771 | break; | |||
772 | } | |||
773 | } | |||
774 | ||||
775 | static ALWAYS_INLINEinline __attribute__((always_inline)) char * | |||
776 | ignore_leading_whitespace(char *buffer) | |||
777 | { | |||
778 | while (lwan_char_isspace(*buffer)) | |||
779 | buffer++; | |||
780 | return buffer; | |||
781 | } | |||
782 | ||||
783 | static ALWAYS_INLINEinline __attribute__((always_inline)) void parse_connection_header(struct lwan_request *request) | |||
784 | { | |||
785 | struct lwan_request_parser_helper *helper = request->helper; | |||
786 | bool_Bool has_keep_alive = false0; | |||
787 | bool_Bool has_close = false0; | |||
788 | ||||
789 | if (!helper->connection.len) | |||
790 | goto out; | |||
791 | ||||
792 | for (const char *p = helper->connection.value; *p; p++) { | |||
793 | STRING_SWITCH_L(p)switch (((string_as_uint32(p)) | (uint32_t)0x20202020)) { | |||
794 | case STR4_INT_L('k','e','e','p')((((uint32_t)(('k') | ('e') << 8 | ('e') << 16 | ( 'p') << 24))) | (uint32_t)0x20202020): | |||
795 | case STR4_INT_L(' ', 'k','e','e')((((uint32_t)((' ') | ('k') << 8 | ('e') << 16 | ( 'e') << 24))) | (uint32_t)0x20202020): | |||
796 | has_keep_alive = true1; | |||
797 | break; | |||
798 | case STR4_INT_L('c','l','o','s')((((uint32_t)(('c') | ('l') << 8 | ('o') << 16 | ( 's') << 24))) | (uint32_t)0x20202020): | |||
799 | case STR4_INT_L(' ', 'c','l','o')((((uint32_t)((' ') | ('c') << 8 | ('l') << 16 | ( 'o') << 24))) | (uint32_t)0x20202020): | |||
800 | has_close = true1; | |||
801 | break; | |||
802 | case STR4_INT_L('u','p','g','r')((((uint32_t)(('u') | ('p') << 8 | ('g') << 16 | ( 'r') << 24))) | (uint32_t)0x20202020): | |||
803 | case STR4_INT_L(' ', 'u','p','g')((((uint32_t)((' ') | ('u') << 8 | ('p') << 16 | ( 'g') << 24))) | (uint32_t)0x20202020): | |||
804 | request->conn->flags |= CONN_IS_UPGRADE; | |||
805 | break; | |||
806 | } | |||
807 | ||||
808 | if (!(p = strchr(p, ','))) | |||
809 | break; | |||
810 | } | |||
811 | ||||
812 | out: | |||
813 | if (LIKELY(!(request->flags & REQUEST_IS_HTTP_1_0))__builtin_expect((!!(!(request->flags & REQUEST_IS_HTTP_1_0 ))), (1))) | |||
814 | has_keep_alive = !has_close; | |||
815 | ||||
816 | if (has_keep_alive) { | |||
817 | request->conn->flags |= CONN_IS_KEEP_ALIVE; | |||
818 | } else { | |||
819 | request->conn->flags &= | |||
820 | ~(CONN_IS_KEEP_ALIVE | CONN_SENT_CONNECTION_HEADER); | |||
821 | } | |||
822 | } | |||
823 | ||||
824 | #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) | |||
825 | static void save_to_corpus_for_fuzzing(struct lwan_value buffer) | |||
826 | { | |||
827 | struct lwan_value buffer_copy; | |||
828 | char corpus_name[PATH_MAX4096]; | |||
829 | const char *crlfcrlf; | |||
830 | int fd; | |||
831 | ||||
832 | if (!(crlfcrlf = memmem(buffer.value, buffer.len, "\r\n\r\n", 4))) | |||
833 | return; | |||
834 | buffer.len = (size_t)(crlfcrlf - buffer.value + 4); | |||
835 | ||||
836 | try_another_file_name: | |||
837 | buffer_copy = buffer; | |||
838 | ||||
839 | snprintf(corpus_name, sizeof(corpus_name), "corpus-request-%d", rand()); | |||
840 | ||||
841 | fd = open(corpus_name, O_WRONLY01 | O_CLOEXEC02000000 | O_CREAT0100 | O_EXCL0200, 0644); | |||
842 | if (fd < 0) | |||
843 | goto try_another_file_name; | |||
844 | ||||
845 | while (buffer_copy.len) { | |||
846 | ssize_t r = write(fd, buffer_copy.value, buffer_copy.len); | |||
847 | ||||
848 | if (r < 0) { | |||
849 | if (errno(*__errno_location ()) == EAGAIN11 || errno(*__errno_location ()) == EINTR4) | |||
850 | continue; | |||
851 | ||||
852 | close(fd); | |||
853 | unlink(corpus_name); | |||
854 | goto try_another_file_name; | |||
855 | } | |||
856 | ||||
857 | buffer_copy.value += r; | |||
858 | buffer_copy.len -= r; | |||
859 | } | |||
860 | ||||
861 | close(fd); | |||
862 | lwan_status_debug("Request saved to %s", corpus_name)lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 862, __FUNCTION__, "Request saved to %s", corpus_name); | |||
863 | } | |||
864 | #endif | |||
865 | ||||
866 | static enum lwan_http_status | |||
867 | client_read(struct lwan_request *request, | |||
868 | struct lwan_value *buffer, | |||
869 | const size_t want_to_read, | |||
870 | enum lwan_read_finalizer (*finalizer)(const struct lwan_value *buffer, | |||
871 | size_t want_to_read, | |||
872 | const struct lwan_request *request, | |||
873 | int n_packets)) | |||
874 | { | |||
875 | struct lwan_request_parser_helper *helper = request->helper; | |||
876 | int n_packets = 0; | |||
877 | ||||
878 | if (helper->next_request) { | |||
879 | const size_t next_request_len = (size_t)(helper->next_request - buffer->value); | |||
880 | size_t new_len; | |||
881 | ||||
882 | if (__builtin_sub_overflow(buffer->len, next_request_len, &new_len)) { | |||
883 | helper->next_request = NULL((void*)0); | |||
884 | } else if (new_len) { | |||
885 | /* FIXME: This memmove() could be eventually removed if a better | |||
886 | * stucture (maybe a ringbuffer, reading with readv(), and each | |||
887 | * pointer is coro_strdup() if they wrap around?) were used for | |||
888 | * the request buffer. */ | |||
889 | buffer->len = new_len; | |||
890 | memmove(buffer->value, helper->next_request, new_len); | |||
891 | goto try_to_finalize; | |||
892 | } | |||
893 | } | |||
894 | ||||
895 | for (buffer->len = 0;; n_packets++) { | |||
896 | size_t to_read = (size_t)(want_to_read - buffer->len); | |||
897 | ||||
898 | if (UNLIKELY(to_read == 0)__builtin_expect(((to_read == 0)), (0))) | |||
899 | return HTTP_TOO_LARGE; | |||
900 | ||||
901 | ssize_t n = recv(request->fd, buffer->value + buffer->len, to_read, 0); | |||
902 | if (UNLIKELY(n <= 0)__builtin_expect(((n <= 0)), (0))) { | |||
903 | if (n < 0) { | |||
904 | switch (errno(*__errno_location ())) { | |||
905 | case EINTR4: | |||
906 | case EAGAIN11: | |||
907 | yield_and_read_again: | |||
908 | coro_yield(request->conn->coro, CONN_CORO_WANT_READ); | |||
909 | continue; | |||
910 | } | |||
911 | ||||
912 | /* Unexpected error before reading anything */ | |||
913 | if (UNLIKELY(!buffer->len)__builtin_expect(((!buffer->len)), (0))) | |||
914 | return HTTP_BAD_REQUEST; | |||
915 | } | |||
916 | ||||
917 | /* Client shut down orderly (n = 0), or unrecoverable error (n < 0); | |||
918 | * shut down coro. */ | |||
919 | break; | |||
920 | } | |||
921 | ||||
922 | buffer->len += (size_t)n; | |||
923 | ||||
924 | try_to_finalize: | |||
925 | switch (finalizer(buffer, want_to_read, request, n_packets)) { | |||
926 | case FINALIZER_DONE: | |||
927 | buffer->value[buffer->len] = '\0'; | |||
928 | #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) | |||
929 | save_to_corpus_for_fuzzing(*buffer); | |||
930 | #endif | |||
931 | return HTTP_OK; | |||
932 | ||||
933 | case FINALIZER_TRY_AGAIN: | |||
934 | goto yield_and_read_again; | |||
935 | ||||
936 | case FINALIZER_TIMEOUT: | |||
937 | return HTTP_TIMEOUT; | |||
938 | } | |||
939 | } | |||
940 | ||||
941 | coro_yield(request->conn->coro, CONN_CORO_ABORT); | |||
942 | __builtin_unreachable(); | |||
943 | return HTTP_INTERNAL_ERROR; | |||
944 | } | |||
945 | ||||
946 | static enum lwan_read_finalizer | |||
947 | read_request_finalizer_from_helper(const struct lwan_value *buffer, | |||
948 | struct lwan_request_parser_helper *helper, | |||
949 | int n_packets, | |||
950 | bool_Bool allow_proxy_reqs) | |||
951 | { | |||
952 | static const size_t min_proxied_request_size = | |||
953 | MIN_REQUEST_SIZE(sizeof("GET / HTTP/1.1\r\n\r\n") - 1) + sizeof(struct proxy_header_v2); | |||
954 | ||||
955 | if (LIKELY(buffer->len >= MIN_REQUEST_SIZE)__builtin_expect((!!(buffer->len >= (sizeof("GET / HTTP/1.1\r\n\r\n" ) - 1))), (1))) { | |||
956 | STRING_SWITCH (buffer->value + buffer->len - 4)switch (string_as_uint32(buffer->value + buffer->len - 4 )) { | |||
957 | case STR4_INT('\r', '\n', '\r', '\n')((uint32_t)(('\r') | ('\n') << 8 | ('\r') << 16 | ('\n') << 24)): | |||
958 | return FINALIZER_DONE; | |||
959 | } | |||
960 | } | |||
961 | ||||
962 | char *crlfcrlf = memmem(buffer->value, buffer->len, "\r\n\r\n", 4); | |||
963 | if (LIKELY(crlfcrlf)__builtin_expect((!!(crlfcrlf)), (1))) { | |||
964 | if (LIKELY(helper->next_request)__builtin_expect((!!(helper->next_request)), (1))) { | |||
965 | helper->next_request = NULL((void*)0); | |||
966 | return FINALIZER_DONE; | |||
967 | } | |||
968 | ||||
969 | const size_t crlfcrlf_to_base = (size_t)(crlfcrlf - buffer->value); | |||
970 | if (crlfcrlf_to_base >= MIN_REQUEST_SIZE(sizeof("GET / HTTP/1.1\r\n\r\n") - 1) - 4) | |||
971 | return FINALIZER_DONE; | |||
972 | ||||
973 | if (buffer->len > min_proxied_request_size && allow_proxy_reqs) { | |||
974 | /* FIXME: Checking for PROXYv2 protocol header here is a layering | |||
975 | * violation. */ | |||
976 | STRING_SWITCH_LARGE (crlfcrlf + 4)switch (string_as_uint64(crlfcrlf + 4)) { | |||
977 | case STR8_INT(0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a)((uint64_t)((uint32_t)((0x00) | (0x0d) << 8 | (0x0a) << 16 | (0x51) << 24)) | (uint64_t)((uint32_t)((0x55) | ( 0x49) << 8 | (0x54) << 16 | (0x0a) << 24)) << 32): | |||
978 | return FINALIZER_DONE; | |||
979 | } | |||
980 | } | |||
981 | } | |||
982 | ||||
983 | /* Yield a timeout error to avoid clients being intentionally slow and | |||
984 | * hogging the server. (Clients can't only connect and do nothing, they | |||
985 | * need to send data, otherwise the timeout queue timer will kick in and | |||
986 | * close the connection. Limit the number of packets to avoid them sending | |||
987 | * just a byte at a time.) See lwan_calculate_n_packets() to see how this is | |||
988 | * calculated. */ | |||
989 | if (UNLIKELY(n_packets > helper->error_when_n_packets)__builtin_expect(((n_packets > helper->error_when_n_packets )), (0))) | |||
990 | return FINALIZER_TIMEOUT; | |||
991 | ||||
992 | return FINALIZER_TRY_AGAIN; | |||
993 | } | |||
994 | ||||
995 | static inline enum lwan_read_finalizer | |||
996 | read_request_finalizer(const struct lwan_value *buffer, | |||
997 | size_t want_to_read __attribute__((unused)), | |||
998 | const struct lwan_request *request, | |||
999 | int n_packets) | |||
1000 | { | |||
1001 | return read_request_finalizer_from_helper( | |||
1002 | buffer, request->helper, n_packets, | |||
1003 | request->flags & REQUEST_ALLOW_PROXY_REQS); | |||
1004 | } | |||
1005 | ||||
1006 | static ALWAYS_INLINEinline __attribute__((always_inline)) enum lwan_http_status | |||
1007 | read_request(struct lwan_request *request) | |||
1008 | { | |||
1009 | return client_read(request, request->helper->buffer, | |||
1010 | DEFAULT_BUFFER_SIZE4096 - 1 /* -1 for NUL byte */, | |||
1011 | read_request_finalizer); | |||
1012 | } | |||
1013 | ||||
1014 | static enum lwan_read_finalizer | |||
1015 | body_data_finalizer(const struct lwan_value *buffer, | |||
1016 | size_t want_to_read, | |||
1017 | const struct lwan_request *request, | |||
1018 | int n_packets) | |||
1019 | { | |||
1020 | const struct lwan_request_parser_helper *helper = request->helper; | |||
1021 | ||||
1022 | if (want_to_read == buffer->len) | |||
1023 | return FINALIZER_DONE; | |||
1024 | ||||
1025 | /* For POST requests, the body can be larger, and due to small MTUs on | |||
1026 | * most ethernet connections, responding with a timeout solely based on | |||
1027 | * number of packets doesn't work. Use keepalive timeout instead. */ | |||
1028 | if (UNLIKELY(time(NULL) > helper->error_when_time)__builtin_expect(((time(((void*)0)) > helper->error_when_time )), (0))) | |||
1029 | return FINALIZER_TIMEOUT; | |||
1030 | ||||
1031 | /* In addition to time, also estimate the number of packets based on an | |||
1032 | * usual MTU value and the request body size. */ | |||
1033 | if (UNLIKELY(n_packets > helper->error_when_n_packets)__builtin_expect(((n_packets > helper->error_when_n_packets )), (0))) | |||
1034 | return FINALIZER_TIMEOUT; | |||
1035 | ||||
1036 | return FINALIZER_TRY_AGAIN; | |||
1037 | } | |||
1038 | ||||
1039 | static const char *is_dir(const char *v) | |||
1040 | { | |||
1041 | struct stat st; | |||
1042 | ||||
1043 | if (!v) | |||
1044 | return NULL((void*)0); | |||
1045 | ||||
1046 | if (*v != '/') | |||
1047 | return NULL((void*)0); | |||
1048 | ||||
1049 | if (stat(v, &st) < 0) | |||
1050 | return NULL((void*)0); | |||
1051 | ||||
1052 | if (!S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000))) | |||
1053 | return NULL((void*)0); | |||
1054 | ||||
1055 | if (!(st.st_mode & S_ISVTX01000)) { | |||
1056 | lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1059, __FUNCTION__, "Using %s as temporary directory, but it doesn't have " "the sticky bit set.", v) | |||
1057 | "Using %s as temporary directory, but it doesn't have "lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1059, __FUNCTION__, "Using %s as temporary directory, but it doesn't have " "the sticky bit set.", v) | |||
1058 | "the sticky bit set.",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1059, __FUNCTION__, "Using %s as temporary directory, but it doesn't have " "the sticky bit set.", v) | |||
1059 | v)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1059, __FUNCTION__, "Using %s as temporary directory, but it doesn't have " "the sticky bit set.", v); | |||
1060 | } | |||
1061 | ||||
1062 | return v; | |||
1063 | } | |||
1064 | ||||
1065 | static const char *is_dir_good_for_tmp(const char *v) | |||
1066 | { | |||
1067 | struct statfs sb; | |||
1068 | ||||
1069 | v = is_dir(v); | |||
1070 | if (!v) | |||
1071 | return NULL((void*)0); | |||
1072 | ||||
1073 | if (!statfs(v, &sb) && sb.f_type == TMPFS_MAGIC0x01021994) { | |||
1074 | lwan_status_warning("%s is a tmpfs filesystem, "lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1075, __FUNCTION__, "%s is a tmpfs filesystem, " "not considering it" , v) | |||
1075 | "not considering it", v)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1075, __FUNCTION__, "%s is a tmpfs filesystem, " "not considering it" , v); | |||
1076 | return NULL((void*)0); | |||
1077 | } | |||
1078 | ||||
1079 | return v; | |||
1080 | } | |||
1081 | ||||
1082 | static const char *temp_dir; | |||
1083 | static const size_t body_buffer_temp_file_thresh = 1<<20; | |||
1084 | ||||
1085 | static const char * | |||
1086 | get_temp_dir(void) | |||
1087 | { | |||
1088 | const char *tmpdir; | |||
1089 | ||||
1090 | tmpdir = is_dir_good_for_tmp(secure_getenv("TMPDIR")); | |||
1091 | if (tmpdir) | |||
1092 | return tmpdir; | |||
1093 | ||||
1094 | tmpdir = is_dir_good_for_tmp(secure_getenv("TMP")); | |||
1095 | if (tmpdir) | |||
1096 | return tmpdir; | |||
1097 | ||||
1098 | tmpdir = is_dir_good_for_tmp(secure_getenv("TEMP")); | |||
1099 | if (tmpdir) | |||
1100 | return tmpdir; | |||
1101 | ||||
1102 | tmpdir = is_dir_good_for_tmp("/var/tmp"); | |||
1103 | if (tmpdir) | |||
1104 | return tmpdir; | |||
1105 | ||||
1106 | tmpdir = is_dir_good_for_tmp(P_tmpdir"/tmp"); | |||
1107 | if (tmpdir) | |||
1108 | return tmpdir; | |||
1109 | ||||
1110 | lwan_status_warning("Temporary directory could not be determined. POST "lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1112, __FUNCTION__, "Temporary directory could not be determined. POST " "or PUT requests over %zu bytes bytes will fail.", body_buffer_temp_file_thresh ) | |||
1111 | "or PUT requests over %zu bytes bytes will fail.",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1112, __FUNCTION__, "Temporary directory could not be determined. POST " "or PUT requests over %zu bytes bytes will fail.", body_buffer_temp_file_thresh ) | |||
1112 | body_buffer_temp_file_thresh)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1112, __FUNCTION__, "Temporary directory could not be determined. POST " "or PUT requests over %zu bytes bytes will fail.", body_buffer_temp_file_thresh ); | |||
1113 | return NULL((void*)0); | |||
1114 | } | |||
1115 | ||||
1116 | __attribute__((constructor)) static void initialize_temp_dir(void) | |||
1117 | { | |||
1118 | temp_dir = get_temp_dir(); | |||
1119 | } | |||
1120 | ||||
1121 | static int create_temp_file(void) | |||
1122 | { | |||
1123 | char template[PATH_MAX4096]; | |||
1124 | mode_t prev_mask; | |||
1125 | int ret; | |||
1126 | ||||
1127 | if (UNLIKELY(!temp_dir)__builtin_expect(((!temp_dir)), (0))) | |||
1128 | return -ENOENT2; | |||
1129 | ||||
1130 | #if defined(O_TMPFILE(020000000 | 0200000)) | |||
1131 | int fd = open(temp_dir, | |||
1132 | O_TMPFILE(020000000 | 0200000) | O_CREAT0100 | O_RDWR02 | O_EXCL0200 | O_CLOEXEC02000000 | | |||
1133 | O_NOFOLLOW0400000 | O_NOATIME01000000, | |||
1134 | S_IRUSR0400 | S_IWUSR0200); | |||
1135 | if (LIKELY(fd >= 0)__builtin_expect((!!(fd >= 0)), (1))) | |||
1136 | return fd; | |||
1137 | #endif | |||
1138 | ||||
1139 | ret = snprintf(template, sizeof(template), "%s/lwanXXXXXX", temp_dir); | |||
1140 | if (UNLIKELY(ret < 0 || ret >= (int)sizeof(template))__builtin_expect(((ret < 0 || ret >= (int)sizeof(template ))), (0))) | |||
1141 | return -EOVERFLOW75; | |||
1142 | ||||
1143 | prev_mask = umask_for_tmpfile(S_IRUSR | S_IWUSR)({ (void)(0400 | 0200); 0U; }); | |||
1144 | ret = mkostemp(template, O_CLOEXEC02000000); | |||
1145 | umask_for_tmpfile(prev_mask)({ (void)(prev_mask); 0U; }); | |||
1146 | ||||
1147 | if (LIKELY(ret >= 0)__builtin_expect((!!(ret >= 0)), (1))) | |||
1148 | unlink(template); | |||
1149 | ||||
1150 | return ret; | |||
1151 | } | |||
1152 | ||||
1153 | struct file_backed_buffer { | |||
1154 | void *ptr; | |||
1155 | size_t size; | |||
1156 | }; | |||
1157 | ||||
1158 | static void | |||
1159 | free_body_buffer(void *data) | |||
1160 | { | |||
1161 | struct file_backed_buffer *buf = data; | |||
1162 | ||||
1163 | munmap(buf->ptr, buf->size); | |||
1164 | free(buf); | |||
1165 | } | |||
1166 | ||||
1167 | static void* | |||
1168 | alloc_body_buffer(struct coro *coro, size_t size, bool_Bool allow_file) | |||
1169 | { | |||
1170 | struct file_backed_buffer *buf; | |||
1171 | void *ptr = (void *)MAP_FAILED((void *) -1); | |||
1172 | int fd; | |||
1173 | ||||
1174 | if (LIKELY(size < body_buffer_temp_file_thresh)__builtin_expect((!!(size < body_buffer_temp_file_thresh)) , (1))) { | |||
1175 | ptr = coro_malloc(coro, size); | |||
1176 | ||||
1177 | if (LIKELY(ptr)__builtin_expect((!!(ptr)), (1))) | |||
1178 | return ptr; | |||
1179 | } | |||
1180 | ||||
1181 | if (UNLIKELY(!allow_file)__builtin_expect(((!allow_file)), (0))) | |||
1182 | return NULL((void*)0); | |||
1183 | ||||
1184 | fd = create_temp_file(); | |||
1185 | if (UNLIKELY(fd < 0)__builtin_expect(((fd < 0)), (0))) | |||
1186 | return NULL((void*)0); | |||
1187 | ||||
1188 | if (UNLIKELY(ftruncate(fd, (off_t)size) < 0)__builtin_expect(((ftruncate(fd, (off_t)size) < 0)), (0))) { | |||
1189 | close(fd); | |||
1190 | return NULL((void*)0); | |||
1191 | } | |||
1192 | ||||
1193 | if (MAP_HUGETLB0x40000) { | |||
1194 | ptr = mmap(NULL((void*)0), size, PROT_READ0x1 | PROT_WRITE0x2, | |||
1195 | MAP_SHARED0x01 | MAP_HUGETLB0x40000, fd, 0); | |||
1196 | } | |||
1197 | if (UNLIKELY(ptr == MAP_FAILED)__builtin_expect(((ptr == ((void *) -1))), (0))) | |||
1198 | ptr = mmap(NULL((void*)0), size, PROT_READ0x1 | PROT_WRITE0x2, MAP_SHARED0x01, fd, 0); | |||
1199 | close(fd); | |||
1200 | if (UNLIKELY(ptr == MAP_FAILED)__builtin_expect(((ptr == ((void *) -1))), (0))) | |||
1201 | return NULL((void*)0); | |||
1202 | ||||
1203 | buf = coro_malloc_full(coro, sizeof(*buf), free_body_buffer); | |||
1204 | if (UNLIKELY(!buf)__builtin_expect(((!buf)), (0))) { | |||
1205 | munmap(ptr, size); | |||
1206 | return NULL((void*)0); | |||
1207 | } | |||
1208 | ||||
1209 | buf->ptr = ptr; | |||
1210 | buf->size = size; | |||
1211 | return ptr; | |||
1212 | } | |||
1213 | ||||
1214 | static enum lwan_http_status | |||
1215 | get_remaining_body_data_length(struct lwan_request *request, | |||
1216 | const size_t max_size, | |||
1217 | size_t *total, | |||
1218 | size_t *have) | |||
1219 | { | |||
1220 | struct lwan_request_parser_helper *helper = request->helper; | |||
1221 | long long parsed_size; | |||
1222 | ||||
1223 | if (UNLIKELY(!helper->content_length.value)__builtin_expect(((!helper->content_length.value)), (0))) | |||
1224 | return HTTP_BAD_REQUEST; | |||
1225 | ||||
1226 | parsed_size = parse_long_long(helper->content_length.value, -1); | |||
1227 | if (UNLIKELY(parsed_size < 0)__builtin_expect(((parsed_size < 0)), (0))) | |||
1228 | return HTTP_BAD_REQUEST; | |||
1229 | if (UNLIKELY((size_t)parsed_size >= max_size)__builtin_expect((((size_t)parsed_size >= max_size)), (0))) | |||
1230 | return HTTP_TOO_LARGE; | |||
1231 | if (UNLIKELY(!parsed_size)__builtin_expect(((!parsed_size)), (0))) | |||
1232 | return HTTP_OK; | |||
1233 | ||||
1234 | *total = (size_t)parsed_size; | |||
1235 | ||||
1236 | if (!helper->next_request) { | |||
1237 | *have = 0; | |||
1238 | return HTTP_PARTIAL_CONTENT; | |||
1239 | } | |||
1240 | ||||
1241 | char *buffer_end = helper->buffer->value + helper->buffer->len; | |||
1242 | ||||
1243 | *have = (size_t)(buffer_end - helper->next_request); | |||
1244 | ||||
1245 | if (*have < *total) | |||
1246 | return HTTP_PARTIAL_CONTENT; | |||
1247 | ||||
1248 | helper->body_data.value = helper->next_request; | |||
1249 | helper->body_data.len = *total; | |||
1250 | helper->next_request += *total; | |||
1251 | return HTTP_OK; | |||
1252 | } | |||
1253 | ||||
1254 | static int read_body_data(struct lwan_request *request) | |||
1255 | { | |||
1256 | /* Holy indirection, Batman! */ | |||
1257 | const struct lwan_config *config = &request->conn->thread->lwan->config; | |||
1258 | struct lwan_request_parser_helper *helper = request->helper; | |||
1259 | enum lwan_http_status status; | |||
1260 | size_t total, have, max_data_size; | |||
1261 | bool_Bool allow_temp_file; | |||
1262 | char *new_buffer; | |||
1263 | ||||
1264 | switch (lwan_request_get_method(request)) { | |||
1265 | case REQUEST_METHOD_POST: | |||
1266 | allow_temp_file = config->allow_post_temp_file; | |||
1267 | max_data_size = config->max_post_data_size; | |||
1268 | break; | |||
1269 | case REQUEST_METHOD_PUT: | |||
1270 | allow_temp_file = config->allow_put_temp_file; | |||
1271 | max_data_size = config->max_put_data_size; | |||
1272 | break; | |||
1273 | default: | |||
1274 | return -HTTP_NOT_ALLOWED; | |||
1275 | } | |||
1276 | ||||
1277 | status = | |||
1278 | get_remaining_body_data_length(request, max_data_size, &total, &have); | |||
1279 | if (status != HTTP_PARTIAL_CONTENT) | |||
1280 | return -(int)status; | |||
1281 | ||||
1282 | new_buffer = | |||
1283 | alloc_body_buffer(request->conn->coro, total + 1, allow_temp_file); | |||
1284 | if (UNLIKELY(!new_buffer)__builtin_expect(((!new_buffer)), (0))) | |||
1285 | return -HTTP_INTERNAL_ERROR; | |||
1286 | ||||
1287 | if (!(request->flags & REQUEST_IS_HTTP_1_0)) { | |||
1288 | /* §8.2.3 https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html */ | |||
1289 | const char *expect = lwan_request_get_header(request, "Expect"); | |||
1290 | ||||
1291 | if (expect && strncmp(expect, "100-", 4) == 0) { | |||
1292 | static const char continue_header[] = "HTTP/1.1 100 Continue\r\n\r\n"; | |||
1293 | ||||
1294 | lwan_send(request, continue_header, sizeof(continue_header) - 1, 0); | |||
1295 | } | |||
1296 | } | |||
1297 | ||||
1298 | helper->body_data.value = new_buffer; | |||
1299 | helper->body_data.len = total; | |||
1300 | if (have) { | |||
1301 | new_buffer = mempcpy(new_buffer, helper->next_request, have); | |||
1302 | total -= have; | |||
1303 | } | |||
1304 | helper->next_request = NULL((void*)0); | |||
1305 | ||||
1306 | helper->error_when_time = time(NULL((void*)0)) + config->keep_alive_timeout; | |||
1307 | helper->error_when_n_packets = lwan_calculate_n_packets(total); | |||
1308 | ||||
1309 | struct lwan_value buffer = {.value = new_buffer, .len = total}; | |||
1310 | return (int)client_read(request, &buffer, total, body_data_finalizer); | |||
1311 | } | |||
1312 | ||||
1313 | static char * | |||
1314 | parse_proxy_protocol(struct lwan_request *request, char *buffer) | |||
1315 | { | |||
1316 | STRING_SWITCH(buffer)switch (string_as_uint32(buffer)) { | |||
1317 | case STR4_INT('P','R','O','X')((uint32_t)(('P') | ('R') << 8 | ('O') << 16 | ('X' ) << 24)): | |||
1318 | return parse_proxy_protocol_v1(request, buffer); | |||
1319 | case STR4_INT('\x0D','\x0A','\x0D','\x0A')((uint32_t)(('\x0D') | ('\x0A') << 8 | ('\x0D') << 16 | ('\x0A') << 24)): | |||
1320 | return parse_proxy_protocol_v2(request, buffer); | |||
1321 | } | |||
1322 | ||||
1323 | return buffer; | |||
1324 | } | |||
1325 | ||||
1326 | static enum lwan_http_status parse_http_request(struct lwan_request *request) | |||
1327 | { | |||
1328 | struct lwan_request_parser_helper *helper = request->helper; | |||
1329 | char *buffer = helper->buffer->value; | |||
1330 | ||||
1331 | if (request->flags & REQUEST_ALLOW_PROXY_REQS) { | |||
1332 | /* REQUEST_ALLOW_PROXY_REQS will be cleared in lwan_process_request() */ | |||
1333 | ||||
1334 | buffer = parse_proxy_protocol(request, buffer); | |||
1335 | if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0))) | |||
1336 | return HTTP_BAD_REQUEST; | |||
1337 | } | |||
1338 | ||||
1339 | buffer = ignore_leading_whitespace(buffer); | |||
1340 | ||||
1341 | if (UNLIKELY(buffer > helper->buffer->value + helper->buffer->len -__builtin_expect(((buffer > helper->buffer->value + helper ->buffer->len - (sizeof("GET / HTTP/1.1\r\n\r\n") - 1)) ), (0)) | |||
1342 | MIN_REQUEST_SIZE)__builtin_expect(((buffer > helper->buffer->value + helper ->buffer->len - (sizeof("GET / HTTP/1.1\r\n\r\n") - 1)) ), (0))) | |||
1343 | return HTTP_BAD_REQUEST; | |||
1344 | ||||
1345 | char *path = identify_http_method(request, buffer); | |||
1346 | if (UNLIKELY(!path)__builtin_expect(((!path)), (0))) | |||
1347 | return HTTP_NOT_ALLOWED; | |||
1348 | ||||
1349 | buffer = identify_http_path(request, path); | |||
1350 | if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0))) | |||
1351 | return HTTP_BAD_REQUEST; | |||
1352 | ||||
1353 | if (UNLIKELY(!parse_headers(helper, buffer))__builtin_expect(((!parse_headers(helper, buffer))), (0))) | |||
1354 | return HTTP_BAD_REQUEST; | |||
1355 | ||||
1356 | ssize_t decoded_len = url_decode(request->url.value); | |||
1357 | if (UNLIKELY(decoded_len < 0)__builtin_expect(((decoded_len < 0)), (0))) | |||
1358 | return HTTP_BAD_REQUEST; | |||
1359 | request->original_url.len = request->url.len = (size_t)decoded_len; | |||
1360 | ||||
1361 | parse_connection_header(request); | |||
1362 | ||||
1363 | return HTTP_OK; | |||
1364 | } | |||
1365 | ||||
1366 | static enum lwan_http_status | |||
1367 | prepare_websocket_handshake(struct lwan_request *request, char **encoded) | |||
1368 | { | |||
1369 | static const unsigned char websocket_uuid[] = | |||
1370 | "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |||
1371 | unsigned char digest[20]; | |||
1372 | sha1_context ctx; | |||
1373 | ||||
1374 | if (UNLIKELY(request->flags & RESPONSE_SENT_HEADERS)__builtin_expect(((request->flags & RESPONSE_SENT_HEADERS )), (0))) | |||
1375 | return HTTP_INTERNAL_ERROR; | |||
1376 | ||||
1377 | if (UNLIKELY(!(request->conn->flags & CONN_IS_UPGRADE))__builtin_expect(((!(request->conn->flags & CONN_IS_UPGRADE ))), (0))) | |||
1378 | return HTTP_BAD_REQUEST; | |||
1379 | ||||
1380 | const char *upgrade = lwan_request_get_header(request, "Upgrade"); | |||
1381 | if (UNLIKELY(!upgrade || !streq(upgrade, "websocket"))__builtin_expect(((!upgrade || !streq(upgrade, "websocket"))) , (0))) | |||
1382 | return HTTP_BAD_REQUEST; | |||
1383 | ||||
1384 | const char *sec_websocket_key = | |||
1385 | lwan_request_get_header(request, "Sec-WebSocket-Key"); | |||
1386 | if (UNLIKELY(!sec_websocket_key)__builtin_expect(((!sec_websocket_key)), (0))) | |||
1387 | return HTTP_BAD_REQUEST; | |||
1388 | ||||
1389 | const size_t sec_websocket_key_len = strlen(sec_websocket_key); | |||
1390 | if (base64_encoded_len(16) != sec_websocket_key_len) | |||
1391 | return HTTP_BAD_REQUEST; | |||
1392 | if (UNLIKELY(!base64_validate((void *)sec_websocket_key, sec_websocket_key_len))__builtin_expect(((!base64_validate((void *)sec_websocket_key , sec_websocket_key_len))), (0))) | |||
1393 | return HTTP_BAD_REQUEST; | |||
1394 | ||||
1395 | sha1_init(&ctx); | |||
1396 | sha1_update(&ctx, (void *)sec_websocket_key, sec_websocket_key_len); | |||
1397 | sha1_update(&ctx, websocket_uuid, sizeof(websocket_uuid) - 1); | |||
1398 | sha1_finalize(&ctx, digest); | |||
1399 | ||||
1400 | *encoded = (char *)base64_encode(digest, sizeof(digest), NULL((void*)0)); | |||
1401 | return LIKELY(*encoded)__builtin_expect((!!(*encoded)), (1)) ? HTTP_SWITCHING_PROTOCOLS : HTTP_INTERNAL_ERROR; | |||
1402 | } | |||
1403 | ||||
1404 | enum lwan_http_status | |||
1405 | lwan_request_websocket_upgrade(struct lwan_request *request) | |||
1406 | { | |||
1407 | char header_buf[DEFAULT_HEADERS_SIZE2048]; | |||
1408 | size_t header_buf_len; | |||
1409 | char *encoded; | |||
1410 | ||||
1411 | enum lwan_http_status r = prepare_websocket_handshake(request, &encoded); | |||
1412 | if (r != HTTP_SWITCHING_PROTOCOLS) | |||
1413 | return r; | |||
1414 | ||||
1415 | request->flags |= RESPONSE_NO_CONTENT_LENGTH; | |||
1416 | header_buf_len = lwan_prepare_response_header_full( | |||
1417 | request, HTTP_SWITCHING_PROTOCOLS, header_buf, sizeof(header_buf), | |||
1418 | (struct lwan_key_value[]){ | |||
1419 | /* Connection: Upgrade is implicit if conn->flags & CONN_IS_UPGRADE */ | |||
1420 | {.key = "Sec-WebSocket-Accept", .value = encoded}, | |||
1421 | {.key = "Upgrade", .value = "websocket"}, | |||
1422 | {}, | |||
1423 | }); | |||
1424 | free(encoded); | |||
1425 | if (UNLIKELY(!header_buf_len)__builtin_expect(((!header_buf_len)), (0))) | |||
1426 | return HTTP_INTERNAL_ERROR; | |||
1427 | ||||
1428 | request->conn->flags |= CONN_IS_WEBSOCKET; | |||
1429 | lwan_send(request, header_buf, header_buf_len, 0); | |||
1430 | ||||
1431 | return HTTP_SWITCHING_PROTOCOLS; | |||
1432 | } | |||
1433 | ||||
1434 | static inline bool_Bool request_has_body(const struct lwan_request *request) | |||
1435 | { | |||
1436 | /* 3rd bit set in method: request method has body. See lwan.h, | |||
1437 | * definition of FOR_EACH_REQUEST_METHOD() for more info. */ | |||
1438 | return lwan_request_get_method(request) & 1 << 3; | |||
1439 | } | |||
1440 | ||||
1441 | static enum lwan_http_status | |||
1442 | maybe_read_body_data(const struct lwan_url_map *url_map, | |||
1443 | struct lwan_request *request) | |||
1444 | { | |||
1445 | int status = 0; | |||
1446 | ||||
1447 | if (url_map->flags & HANDLER_EXPECTS_BODY_DATA) { | |||
1448 | status = read_body_data(request); | |||
1449 | if (status > 0) | |||
1450 | return (enum lwan_http_status)status; | |||
1451 | } | |||
1452 | ||||
1453 | /* Instead of trying to read the body here, which will require | |||
1454 | * us to allocate and read potentially a lot of bytes, force | |||
1455 | * this connection to be closed as soon as we send a "not allowed" | |||
1456 | * response. */ | |||
1457 | request->conn->flags &= ~CONN_IS_KEEP_ALIVE; | |||
1458 | ||||
1459 | if (status < 0) { | |||
1460 | status = -status; | |||
1461 | return (enum lwan_http_status)status; | |||
1462 | } | |||
1463 | ||||
1464 | return HTTP_NOT_ALLOWED; | |||
1465 | } | |||
1466 | ||||
1467 | static enum lwan_http_status prepare_for_response(const struct lwan_url_map *url_map, | |||
1468 | struct lwan_request *request) | |||
1469 | { | |||
1470 | request->url.value += url_map->prefix_len; | |||
1471 | request->url.len -= url_map->prefix_len; | |||
1472 | while (*request->url.value == '/' && request->url.len > 0) { | |||
1473 | request->url.value++; | |||
1474 | request->url.len--; | |||
1475 | } | |||
1476 | ||||
1477 | if (UNLIKELY(url_map->flags & HANDLER_MUST_AUTHORIZE)__builtin_expect(((url_map->flags & HANDLER_MUST_AUTHORIZE )), (0))) { | |||
1478 | if (!lwan_http_authorize_urlmap(request, url_map)) | |||
1479 | return HTTP_NOT_AUTHORIZED; | |||
1480 | } | |||
1481 | ||||
1482 | if (UNLIKELY(request_has_body(request))__builtin_expect(((request_has_body(request))), (0))) | |||
1483 | return maybe_read_body_data(url_map, request); | |||
1484 | ||||
1485 | return HTTP_OK; | |||
1486 | } | |||
1487 | ||||
1488 | static bool_Bool handle_rewrite(struct lwan_request *request) | |||
1489 | { | |||
1490 | struct lwan_request_parser_helper *helper = request->helper; | |||
1491 | ||||
1492 | request->flags &= ~RESPONSE_URL_REWRITTEN; | |||
1493 | ||||
1494 | find_query_string(request, request->url.value + request->url.len); | |||
1495 | ||||
1496 | helper->urls_rewritten++; | |||
1497 | if (UNLIKELY(helper->urls_rewritten > 4)__builtin_expect(((helper->urls_rewritten > 4)), (0))) { | |||
1498 | lwan_default_response(request, HTTP_INTERNAL_ERROR); | |||
1499 | return false0; | |||
1500 | } | |||
1501 | ||||
1502 | return true1; | |||
1503 | } | |||
1504 | ||||
1505 | const char *lwan_request_get_method_str(const struct lwan_request *request) | |||
1506 | { | |||
1507 | #define GENERATE_CASE_STMT(upper, lower, mask, constant, probability) \ | |||
1508 | case REQUEST_METHOD_##upper: \ | |||
1509 | return #upper; | |||
1510 | ||||
1511 | switch (lwan_request_get_method(request)) { | |||
1512 | FOR_EACH_REQUEST_METHOD(GENERATE_CASE_STMT)GENERATE_CASE_STMT(GET, get, (1 << 0), (((uint32_t)(('G' ) | ('E') << 8 | ('T') << 16 | (' ') << 24) )), 0.6) GENERATE_CASE_STMT(POST, post, (1 << 3 | 1 << 1 | 1 << 0), (((uint32_t)(('P') | ('O') << 8 | ( 'S') << 16 | ('T') << 24))), 0.2) GENERATE_CASE_STMT (HEAD, head, (1 << 1), (((uint32_t)(('H') | ('E') << 8 | ('A') << 16 | ('D') << 24))), 0.2) GENERATE_CASE_STMT (OPTIONS, options, (1 << 2), (((uint32_t)(('O') | ('P') << 8 | ('T') << 16 | ('I') << 24))), 0.1) GENERATE_CASE_STMT (DELETE, delete, (1 << 1 | 1 << 2), (((uint32_t)( ('D') | ('E') << 8 | ('L') << 16 | ('E') << 24))), 0.1) GENERATE_CASE_STMT(PUT, put, (1 << 3 | 1 << 2 | 1 << 0), (((uint32_t)(('P') | ('U') << 8 | ( 'T') << 16 | (' ') << 24))), 0.1) | |||
1513 | default: | |||
1514 | return "UNKNOWN"; | |||
1515 | } | |||
1516 | #undef GENERATE_CASE_STMT | |||
1517 | } | |||
1518 | ||||
1519 | #ifndef NDEBUG | |||
1520 | static void log_request(struct lwan_request *request, | |||
1521 | enum lwan_http_status status, | |||
1522 | double time_to_read_request, | |||
1523 | double time_to_process_request) | |||
1524 | { | |||
1525 | char ip_buffer[INET6_ADDRSTRLEN46]; | |||
1526 | ||||
1527 | lwan_status_debug(lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request) | |||
1528 | "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)",lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request) | |||
1529 | lwan_request_get_remote_address(request, ip_buffer),lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request) | |||
1530 | request->conn->thread->date.date, lwan_request_get_id(request),lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request) | |||
1531 | lwan_request_get_method_str(request), request->original_url.value,lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request) | |||
1532 | request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status,lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request) | |||
1533 | request->response.mime_type, time_to_read_request,lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request) | |||
1534 | time_to_process_request)lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1534, __FUNCTION__, "%s [%s] %016lx \"%s %s HTTP/%s\" %d %s (r:%.3fms p:%.3fms)" , lwan_request_get_remote_address(request, ip_buffer), request ->conn->thread->date.date, lwan_request_get_id(request ), lwan_request_get_method_str(request), request->original_url .value, request->flags & REQUEST_IS_HTTP_1_0 ? "1.0" : "1.1", status, request->response.mime_type, time_to_read_request , time_to_process_request); | |||
1535 | } | |||
1536 | #else | |||
1537 | #define log_request(...) | |||
1538 | #endif | |||
1539 | ||||
1540 | #ifndef NDEBUG | |||
1541 | static struct timespec current_precise_monotonic_timespec(void) | |||
1542 | { | |||
1543 | struct timespec now; | |||
1544 | ||||
1545 | if (UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &now) < 0)__builtin_expect(((clock_gettime(1, &now) < 0)), (0))) { | |||
1546 | lwan_status_perror("clock_gettime")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1546, __FUNCTION__, "clock_gettime"); | |||
1547 | return (struct timespec){}; | |||
1548 | } | |||
1549 | ||||
1550 | return now; | |||
1551 | } | |||
1552 | ||||
1553 | static double elapsed_time_ms(const struct timespec then) | |||
1554 | { | |||
1555 | const struct timespec now = current_precise_monotonic_timespec(); | |||
1556 | struct timespec diff = { | |||
1557 | .tv_sec = now.tv_sec - then.tv_sec, | |||
1558 | .tv_nsec = now.tv_nsec - then.tv_nsec, | |||
1559 | }; | |||
1560 | ||||
1561 | if (diff.tv_nsec < 0) { | |||
1562 | diff.tv_sec--; | |||
1563 | diff.tv_nsec += 1000000000l; | |||
1564 | } | |||
1565 | ||||
1566 | return (double)diff.tv_sec / 1000.0 + (double)diff.tv_nsec / 1000000.0; | |||
1567 | } | |||
1568 | #endif | |||
1569 | ||||
1570 | void lwan_process_request(struct lwan *l, struct lwan_request *request) | |||
1571 | { | |||
1572 | enum lwan_http_status status; | |||
1573 | struct lwan_url_map *url_map; | |||
1574 | ||||
1575 | #ifndef NDEBUG | |||
1576 | struct timespec request_read_begin_time = current_precise_monotonic_timespec(); | |||
1577 | #endif | |||
1578 | status = read_request(request); | |||
1579 | ||||
1580 | #ifndef NDEBUG | |||
1581 | double time_to_read_request = elapsed_time_ms(request_read_begin_time); | |||
1582 | ||||
1583 | struct timespec request_begin_time = current_precise_monotonic_timespec(); | |||
1584 | #endif | |||
1585 | if (UNLIKELY(status != HTTP_OK)__builtin_expect(((status != HTTP_OK)), (0))) { | |||
| ||||
1586 | /* If read_request() returns any error at this point, it's probably | |||
1587 | * better to just send an error response and abort the coroutine and | |||
1588 | * let the client handle the error instead: we don't have | |||
1589 | * information to even log the request because it has not been | |||
1590 | * parsed yet at this stage. Even if there are other requests waiting | |||
1591 | * in the pipeline, this seems like the safer thing to do. */ | |||
1592 | request->conn->flags &= ~CONN_IS_KEEP_ALIVE; | |||
1593 | lwan_default_response(request, status); | |||
1594 | /* Let process_request_coro() gracefully close the connection. */ | |||
1595 | return; | |||
1596 | } | |||
1597 | ||||
1598 | status = parse_http_request(request); | |||
1599 | if (UNLIKELY(status != HTTP_OK)__builtin_expect(((status != HTTP_OK)), (0))) | |||
1600 | goto log_and_return; | |||
1601 | ||||
1602 | lookup_again: | |||
1603 | url_map = lwan_trie_lookup_prefix(&l->url_map_trie, request->url.value); | |||
1604 | if (UNLIKELY(!url_map)__builtin_expect(((!url_map)), (0))) { | |||
1605 | status = HTTP_NOT_FOUND; | |||
1606 | goto log_and_return; | |||
1607 | } | |||
1608 | ||||
1609 | status = prepare_for_response(url_map, request); | |||
1610 | if (UNLIKELY(status != HTTP_OK)__builtin_expect(((status != HTTP_OK)), (0))) | |||
1611 | goto log_and_return; | |||
1612 | ||||
1613 | status = url_map->handler(request, &request->response, url_map->data); | |||
1614 | if (UNLIKELY(url_map->flags & HANDLER_CAN_REWRITE_URL)__builtin_expect(((url_map->flags & HANDLER_CAN_REWRITE_URL )), (0))) { | |||
1615 | if (request->flags & RESPONSE_URL_REWRITTEN) { | |||
1616 | if (LIKELY(handle_rewrite(request))__builtin_expect((!!(handle_rewrite(request))), (1))) | |||
1617 | goto lookup_again; | |||
1618 | return; | |||
1619 | } | |||
1620 | } | |||
1621 | ||||
1622 | log_and_return: | |||
1623 | lwan_response(request, status); | |||
1624 | ||||
1625 | log_request(request, status, time_to_read_request, elapsed_time_ms(request_begin_time)); | |||
1626 | } | |||
1627 | ||||
1628 | static inline void * | |||
1629 | value_lookup(const struct lwan_key_value_array *array, const char *key) | |||
1630 | { | |||
1631 | const struct lwan_array *la = (const struct lwan_array *)array; | |||
1632 | ||||
1633 | if (LIKELY(la->elements)__builtin_expect((!!(la->elements)), (1))) { | |||
1634 | struct lwan_key_value k = { .key = (char *)key }; | |||
1635 | struct lwan_key_value *entry; | |||
1636 | ||||
1637 | entry = bsearch(&k, la->base, la->elements, sizeof(k), key_value_compare); | |||
1638 | if (LIKELY(entry)__builtin_expect((!!(entry)), (1))) | |||
1639 | return entry->value; | |||
1640 | } | |||
1641 | ||||
1642 | return NULL((void*)0); | |||
1643 | } | |||
1644 | ||||
1645 | const char *lwan_request_get_query_param(struct lwan_request *request, | |||
1646 | const char *key) | |||
1647 | { | |||
1648 | return value_lookup(lwan_request_get_query_params(request), key); | |||
1649 | } | |||
1650 | ||||
1651 | const char *lwan_request_get_post_param(struct lwan_request *request, | |||
1652 | const char *key) | |||
1653 | { | |||
1654 | return value_lookup(lwan_request_get_post_params(request), key); | |||
1655 | } | |||
1656 | ||||
1657 | const char *lwan_request_get_cookie(struct lwan_request *request, | |||
1658 | const char *key) | |||
1659 | { | |||
1660 | return value_lookup(lwan_request_get_cookies(request), key); | |||
1661 | } | |||
1662 | ||||
1663 | const char * | |||
1664 | lwan_request_get_header_from_helper(struct lwan_request_parser_helper *helper, | |||
1665 | const char *header) | |||
1666 | { | |||
1667 | const size_t header_len = strlen(header); | |||
1668 | const size_t header_len_with_separator = | |||
1669 | header_len + HEADER_VALUE_SEPARATOR_LEN(sizeof(": ") - 1); | |||
1670 | ||||
1671 | assert(strchr(header, ':') == NULL)((void) sizeof ((strchr(header, ':') == ((void*)0)) ? 1 : 0), __extension__ ({ if (strchr(header, ':') == ((void*)0)) ; else __assert_fail ("strchr(header, ':') == NULL", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1671, __extension__ __PRETTY_FUNCTION__); })); | |||
1672 | ||||
1673 | for (size_t i = 0; i < helper->n_header_start; i++) { | |||
1674 | const char *start = helper->header_start[i]; | |||
1675 | char *end = helper->header_start[i + 1] - HEADER_TERMINATOR_LEN(sizeof("\r\n") - 1); | |||
1676 | ||||
1677 | if (UNLIKELY((size_t)(end - start) < header_len_with_separator)__builtin_expect((((size_t)(end - start) < header_len_with_separator )), (0))) | |||
1678 | continue; | |||
1679 | ||||
1680 | STRING_SWITCH_SMALL (start + header_len)switch (string_as_uint16(start + header_len)) { | |||
1681 | case STR2_INT(':', ' ')((uint16_t)((':') | (' ') << 8)): | |||
1682 | if (!strncasecmp(start, header, header_len)) { | |||
1683 | *end = '\0'; | |||
1684 | return start + header_len_with_separator; | |||
1685 | } | |||
1686 | } | |||
1687 | } | |||
1688 | ||||
1689 | return NULL((void*)0); | |||
1690 | } | |||
1691 | ||||
1692 | inline const char *lwan_request_get_header(struct lwan_request *request, | |||
1693 | const char *header) | |||
1694 | { | |||
1695 | return lwan_request_get_header_from_helper(request->helper, header); | |||
1696 | } | |||
1697 | ||||
1698 | const char *lwan_request_get_host(struct lwan_request *request) | |||
1699 | { | |||
1700 | const struct lwan_request_parser_helper *helper = request->helper; | |||
1701 | ||||
1702 | return helper->host.len ? helper->host.value : NULL((void*)0); | |||
1703 | } | |||
1704 | ||||
1705 | ALWAYS_INLINEinline __attribute__((always_inline)) int | |||
1706 | lwan_connection_get_fd(const struct lwan *lwan, const struct lwan_connection *conn) | |||
1707 | { | |||
1708 | return (int)(intptr_t)(conn - lwan->conns); | |||
1709 | } | |||
1710 | ||||
1711 | const char * | |||
1712 | lwan_request_get_remote_address_and_port(struct lwan_request *request, | |||
1713 | char buffer[static INET6_ADDRSTRLEN46], | |||
1714 | uint16_t *port) | |||
1715 | { | |||
1716 | struct sockaddr_storage non_proxied_addr = {.ss_family = AF_UNSPEC0}; | |||
1717 | struct sockaddr_storage *sock_addr; | |||
1718 | ||||
1719 | *port = 0; | |||
1720 | ||||
1721 | if (request->flags & REQUEST_PROXIED) { | |||
1722 | sock_addr = (struct sockaddr_storage *)&request->proxy->from; | |||
1723 | ||||
1724 | if (UNLIKELY(sock_addr->ss_family == AF_UNSPEC)__builtin_expect(((sock_addr->ss_family == 0)), (0))) { | |||
1725 | static const char unspecified[] = "*unspecified*"; | |||
1726 | ||||
1727 | static_assert_Static_assert(sizeof(unspecified) <= INET6_ADDRSTRLEN46, | |||
1728 | "Enough space for unspecified address family"); | |||
1729 | return memcpy(buffer, unspecified, sizeof(unspecified)); | |||
1730 | } | |||
1731 | } else { | |||
1732 | socklen_t sock_len = sizeof(non_proxied_addr); | |||
1733 | ||||
1734 | sock_addr = &non_proxied_addr; | |||
1735 | ||||
1736 | if (UNLIKELY(getpeername(request->fd, (struct sockaddr *)sock_addr,__builtin_expect(((getpeername(request->fd, (struct sockaddr *)sock_addr, &sock_len) < 0)), (0)) | |||
1737 | &sock_len) < 0)__builtin_expect(((getpeername(request->fd, (struct sockaddr *)sock_addr, &sock_len) < 0)), (0))) { | |||
1738 | return NULL((void*)0); | |||
1739 | } | |||
1740 | } | |||
1741 | ||||
1742 | if (sock_addr->ss_family
| |||
1743 | struct sockaddr_in *sin = (struct sockaddr_in *)sock_addr; | |||
1744 | *port = ntohs(sin->sin_port); | |||
1745 | return inet_ntop(AF_INET2, &sin->sin_addr, buffer, INET6_ADDRSTRLEN46); | |||
1746 | } | |||
1747 | ||||
1748 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sock_addr; | |||
1749 | *port = ntohs(sin6->sin6_port); | |||
| ||||
1750 | return inet_ntop(AF_INET610, &sin6->sin6_addr, buffer, INET6_ADDRSTRLEN46); | |||
1751 | } | |||
1752 | ||||
1753 | const char * | |||
1754 | lwan_request_get_remote_address(struct lwan_request *request, | |||
1755 | char buffer[static INET6_ADDRSTRLEN46]) | |||
1756 | { | |||
1757 | uint16_t port; | |||
1758 | return lwan_request_get_remote_address_and_port(request, buffer, &port); | |||
1759 | } | |||
1760 | ||||
1761 | static void remove_sleep(void *data1, void *data2) | |||
1762 | { | |||
1763 | static const enum lwan_connection_flags suspended_sleep = | |||
1764 | CONN_SUSPENDED | CONN_HAS_REMOVE_SLEEP_DEFER; | |||
1765 | struct timeouts *wheel = data1; | |||
1766 | struct timeout *timeout = data2; | |||
1767 | struct lwan_request *request = | |||
1768 | container_of(timeout, struct lwan_request, timeout)((struct lwan_request *) ((char *)(timeout) - __builtin_offsetof (struct lwan_request, timeout)) + ((typeof(*(timeout)) *)0 != (typeof(((struct lwan_request *)0)->timeout) *)0)); | |||
1769 | ||||
1770 | if ((request->conn->flags & suspended_sleep) == suspended_sleep) | |||
1771 | timeouts_del(wheel, timeout); | |||
1772 | ||||
1773 | request->conn->flags &= ~CONN_HAS_REMOVE_SLEEP_DEFER; | |||
1774 | } | |||
1775 | ||||
1776 | void lwan_request_sleep(struct lwan_request *request, uint64_t ms) | |||
1777 | { | |||
1778 | struct lwan_connection *conn = request->conn; | |||
1779 | struct timeouts *wheel = conn->thread->wheel; | |||
1780 | struct timespec now; | |||
1781 | coro_deferred defer = -1; | |||
1782 | ||||
1783 | /* We need to update the timer wheel right now because | |||
1784 | * a request might have requested to sleep a long time | |||
1785 | * before it was being serviced -- causing the timeout | |||
1786 | * to essentially be a no-op. */ | |||
1787 | if (UNLIKELY(clock_gettime(monotonic_clock_id, &now) < 0)__builtin_expect(((clock_gettime(monotonic_clock_id, &now ) < 0)), (0))) | |||
1788 | lwan_status_critical("Could not get monotonic time")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 1788, __FUNCTION__, "Could not get monotonic time"); | |||
1789 | timeouts_update(wheel, (timeout_t)(now.tv_sec * 1000 + now.tv_nsec / 1000000)); | |||
1790 | ||||
1791 | request->timeout = (struct timeout) {}; | |||
1792 | timeouts_add(wheel, &request->timeout, ms); | |||
1793 | ||||
1794 | if (!(conn->flags & CONN_HAS_REMOVE_SLEEP_DEFER)) { | |||
1795 | defer = coro_defer2(conn->coro, remove_sleep, wheel, &request->timeout); | |||
1796 | conn->flags |= CONN_HAS_REMOVE_SLEEP_DEFER; | |||
1797 | } | |||
1798 | ||||
1799 | coro_yield(conn->coro, CONN_CORO_SUSPEND); | |||
1800 | ||||
1801 | if (defer > 0) | |||
1802 | coro_defer_fire_and_disarm(conn->coro, defer); | |||
1803 | } | |||
1804 | ||||
1805 | ALWAYS_INLINEinline __attribute__((always_inline)) int | |||
1806 | lwan_request_get_range(struct lwan_request *request, off_t *from, off_t *to) | |||
1807 | { | |||
1808 | struct lwan_request_parser_helper *helper = request->helper; | |||
1809 | ||||
1810 | if (!(request->flags & REQUEST_PARSED_RANGE)) { | |||
1811 | parse_range(helper); | |||
1812 | request->flags |= REQUEST_PARSED_RANGE; | |||
1813 | } | |||
1814 | ||||
1815 | if (LIKELY(helper->range.raw.len)__builtin_expect((!!(helper->range.raw.len)), (1))) { | |||
1816 | *from = helper->range.from; | |||
1817 | *to = helper->range.to; | |||
1818 | return 0; | |||
1819 | } | |||
1820 | ||||
1821 | return -ENOENT2; | |||
1822 | } | |||
1823 | ||||
1824 | ALWAYS_INLINEinline __attribute__((always_inline)) int | |||
1825 | lwan_request_get_if_modified_since(struct lwan_request *request, time_t *value) | |||
1826 | { | |||
1827 | struct lwan_request_parser_helper *helper = request->helper; | |||
1828 | ||||
1829 | if (!(request->flags & REQUEST_PARSED_IF_MODIFIED_SINCE)) { | |||
1830 | parse_if_modified_since(helper); | |||
1831 | request->flags |= REQUEST_PARSED_IF_MODIFIED_SINCE; | |||
1832 | } | |||
1833 | ||||
1834 | if (LIKELY(helper->if_modified_since.raw.len)__builtin_expect((!!(helper->if_modified_since.raw.len)), ( 1))) { | |||
1835 | *value = helper->if_modified_since.parsed; | |||
1836 | return 0; | |||
1837 | } | |||
1838 | ||||
1839 | return -ENOENT2; | |||
1840 | } | |||
1841 | ||||
1842 | ALWAYS_INLINEinline __attribute__((always_inline)) const struct lwan_value * | |||
1843 | lwan_request_get_request_body(struct lwan_request *request) | |||
1844 | { | |||
1845 | return &request->helper->body_data; | |||
1846 | } | |||
1847 | ||||
1848 | ALWAYS_INLINEinline __attribute__((always_inline)) const struct lwan_value * | |||
1849 | lwan_request_get_content_type(struct lwan_request *request) | |||
1850 | { | |||
1851 | return &request->helper->content_type; | |||
1852 | } | |||
1853 | ||||
1854 | ALWAYS_INLINEinline __attribute__((always_inline)) const struct lwan_key_value_array * | |||
1855 | lwan_request_get_cookies(struct lwan_request *request) | |||
1856 | { | |||
1857 | if (!(request->flags & REQUEST_PARSED_COOKIES)) { | |||
1858 | parse_cookies(request); | |||
1859 | request->flags |= REQUEST_PARSED_COOKIES; | |||
1860 | } | |||
1861 | ||||
1862 | return &request->helper->cookies; | |||
1863 | } | |||
1864 | ||||
1865 | ALWAYS_INLINEinline __attribute__((always_inline)) const struct lwan_key_value_array * | |||
1866 | lwan_request_get_query_params(struct lwan_request *request) | |||
1867 | { | |||
1868 | if (!(request->flags & REQUEST_PARSED_QUERY_STRING)) { | |||
1869 | parse_query_string(request); | |||
1870 | request->flags |= REQUEST_PARSED_QUERY_STRING; | |||
1871 | } | |||
1872 | ||||
1873 | return &request->helper->query_params; | |||
1874 | } | |||
1875 | ||||
1876 | ALWAYS_INLINEinline __attribute__((always_inline)) const struct lwan_key_value_array * | |||
1877 | lwan_request_get_post_params(struct lwan_request *request) | |||
1878 | { | |||
1879 | if (!(request->flags & REQUEST_PARSED_FORM_DATA)) { | |||
1880 | parse_form_data(request); | |||
1881 | request->flags |= REQUEST_PARSED_FORM_DATA; | |||
1882 | } | |||
1883 | ||||
1884 | return &request->helper->post_params; | |||
1885 | } | |||
1886 | ||||
1887 | ALWAYS_INLINEinline __attribute__((always_inline)) enum lwan_request_flags | |||
1888 | lwan_request_get_accept_encoding(struct lwan_request *request) | |||
1889 | { | |||
1890 | if (!(request->flags & REQUEST_PARSED_ACCEPT_ENCODING)) { | |||
1891 | parse_accept_encoding(request); | |||
1892 | request->flags |= REQUEST_PARSED_ACCEPT_ENCODING; | |||
1893 | } | |||
1894 | ||||
1895 | return request->flags & REQUEST_ACCEPT_MASK; | |||
1896 | } | |||
1897 | ||||
1898 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | |||
1899 | static int useless_coro_for_fuzzing(struct coro *c __attribute__((unused)), | |||
1900 | void *data __attribute__((unused))) | |||
1901 | { | |||
1902 | return 0; | |||
1903 | } | |||
1904 | ||||
1905 | static bool_Bool request_seems_complete(struct lwan_request_parser_helper *helper) | |||
1906 | { | |||
1907 | return read_request_finalizer_from_helper(helper->buffer, helper, 1, | |||
1908 | false0) == FINALIZER_DONE; | |||
1909 | } | |||
1910 | ||||
1911 | __attribute__((used)) int fuzz_parse_http_request(const uint8_t *data, | |||
1912 | size_t length) | |||
1913 | { | |||
1914 | static struct coro_switcher switcher; | |||
1915 | static struct coro *coro; | |||
1916 | static char *header_start[N_HEADER_START64]; | |||
1917 | static char data_copy[32767] = {0}; | |||
1918 | ||||
1919 | if (length > sizeof(data_copy)) | |||
1920 | length = sizeof(data_copy); | |||
1921 | memcpy(data_copy, data, length); | |||
1922 | ||||
1923 | if (!coro) { | |||
1924 | coro = coro_new(&switcher, useless_coro_for_fuzzing, NULL((void*)0)); | |||
1925 | ||||
1926 | lwan_job_thread_init(); | |||
1927 | lwan_http_authorize_init(); | |||
1928 | } | |||
1929 | ||||
1930 | struct lwan_request_parser_helper helper = { | |||
1931 | .buffer = &(struct lwan_value){.value = data_copy, .len = length}, | |||
1932 | .header_start = header_start, | |||
1933 | .error_when_n_packets = 2, | |||
1934 | }; | |||
1935 | struct lwan_connection conn = {.coro = coro}; | |||
1936 | struct lwan_proxy proxy = {}; | |||
1937 | struct lwan_request request = { | |||
1938 | .helper = &helper, | |||
1939 | .conn = &conn, | |||
1940 | .flags = REQUEST_ALLOW_PROXY_REQS, | |||
1941 | .proxy = &proxy, | |||
1942 | }; | |||
1943 | ||||
1944 | /* If the finalizer isn't happy with a request, there's no point in | |||
1945 | * going any further with parsing it. */ | |||
1946 | if (!request_seems_complete(&helper)) | |||
1947 | return 0; | |||
1948 | ||||
1949 | /* client_read() NUL-terminates the string */ | |||
1950 | data_copy[length - 1] = '\0'; | |||
1951 | ||||
1952 | if (parse_http_request(&request) != HTTP_OK) | |||
1953 | return 0; | |||
1954 | ||||
1955 | off_t trash1; | |||
1956 | time_t trash2; | |||
1957 | char *trash3; | |||
1958 | size_t gen = coro_deferred_get_generation(coro); | |||
1959 | ||||
1960 | /* Only pointers were set in helper struct; actually parse them here. */ | |||
1961 | parse_accept_encoding(&request); | |||
1962 | ||||
1963 | /* Requesting these items will force them to be parsed, and also | |||
1964 | * exercise the lookup function. */ | |||
1965 | LWAN_NO_DISCARD(lwan_request_get_header(&request, "Non-Existing-Header"))do { __typeof__(lwan_request_get_header(&request, "Non-Existing-Header" )) no_discard_ = lwan_request_get_header(&request, "Non-Existing-Header" ); __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1966 | ||||
1967 | /* Usually existing short header */ | |||
1968 | LWAN_NO_DISCARD(lwan_request_get_header(&request, "Host"))do { __typeof__(lwan_request_get_header(&request, "Host") ) no_discard_ = lwan_request_get_header(&request, "Host") ; __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1969 | ||||
1970 | LWAN_NO_DISCARD(lwan_request_get_cookie(&request, "Non-Existing-Cookie"))do { __typeof__(lwan_request_get_cookie(&request, "Non-Existing-Cookie" )) no_discard_ = lwan_request_get_cookie(&request, "Non-Existing-Cookie" ); __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1971 | /* Set by some tests */ | |||
1972 | LWAN_NO_DISCARD(lwan_request_get_cookie(&request, "FOO"))do { __typeof__(lwan_request_get_cookie(&request, "FOO")) no_discard_ = lwan_request_get_cookie(&request, "FOO"); __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1973 | ||||
1974 | LWAN_NO_DISCARD(do { __typeof__(lwan_request_get_query_param(&request, "Non-Existing-Query-Param" )) no_discard_ = lwan_request_get_query_param(&request, "Non-Existing-Query-Param" ); __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0) | |||
1975 | lwan_request_get_query_param(&request, "Non-Existing-Query-Param"))do { __typeof__(lwan_request_get_query_param(&request, "Non-Existing-Query-Param" )) no_discard_ = lwan_request_get_query_param(&request, "Non-Existing-Query-Param" ); __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1976 | ||||
1977 | LWAN_NO_DISCARD(do { __typeof__(lwan_request_get_post_param(&request, "Non-Existing-Post-Param" )) no_discard_ = lwan_request_get_post_param(&request, "Non-Existing-Post-Param" ); __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0) | |||
1978 | lwan_request_get_post_param(&request, "Non-Existing-Post-Param"))do { __typeof__(lwan_request_get_post_param(&request, "Non-Existing-Post-Param" )) no_discard_ = lwan_request_get_post_param(&request, "Non-Existing-Post-Param" ); __asm__ __volatile__("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1979 | ||||
1980 | lwan_request_get_range(&request, &trash1, &trash1); | |||
1981 | LWAN_NO_DISCARD(trash1)do { __typeof__(trash1) no_discard_ = trash1; __asm__ __volatile__ ("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1982 | ||||
1983 | lwan_request_get_if_modified_since(&request, &trash2); | |||
1984 | LWAN_NO_DISCARD(trash2)do { __typeof__(trash2) no_discard_ = trash2; __asm__ __volatile__ ("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1985 | ||||
1986 | enum lwan_http_status handshake = | |||
1987 | prepare_websocket_handshake(&request, &trash3); | |||
1988 | LWAN_NO_DISCARD(trash3)do { __typeof__(trash3) no_discard_ = trash3; __asm__ __volatile__ ("" ::"g"(no_discard_) : "memory"); } while (0); | |||
1989 | if (handshake == HTTP_SWITCHING_PROTOCOLS) | |||
1990 | free(trash3); | |||
1991 | ||||
1992 | LWAN_NO_DISCARD(lwan_http_authorize(&request, "Fuzzy Realm", "/dev/null"))do { __typeof__(lwan_http_authorize(&request, "Fuzzy Realm" , "/dev/null")) no_discard_ = lwan_http_authorize(&request , "Fuzzy Realm", "/dev/null"); __asm__ __volatile__("" ::"g"( no_discard_) : "memory"); } while (0); | |||
1993 | ||||
1994 | coro_deferred_run(coro, gen); | |||
1995 | ||||
1996 | return 0; | |||
1997 | } | |||
1998 | #endif | |||
1999 | ||||
2000 | static inline int64_t | |||
2001 | make_async_yield_value(int fd, enum lwan_connection_coro_yield event) | |||
2002 | { | |||
2003 | return (int64_t)(((uint64_t)fd << 32 | event)); | |||
2004 | } | |||
2005 | ||||
2006 | static inline void async_await_fd(struct coro *coro, | |||
2007 | int fd, | |||
2008 | enum lwan_connection_coro_yield events) | |||
2009 | { | |||
2010 | assert(events >= CONN_CORO_ASYNC_AWAIT_READ &&((void) sizeof ((events >= CONN_CORO_ASYNC_AWAIT_READ && events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE) ? 1 : 0), __extension__ ({ if (events >= CONN_CORO_ASYNC_AWAIT_READ && events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE) ; else __assert_fail ("events >= CONN_CORO_ASYNC_AWAIT_READ && events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE" , "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 2011, __extension__ __PRETTY_FUNCTION__); })) | |||
2011 | events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE)((void) sizeof ((events >= CONN_CORO_ASYNC_AWAIT_READ && events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE) ? 1 : 0), __extension__ ({ if (events >= CONN_CORO_ASYNC_AWAIT_READ && events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE) ; else __assert_fail ("events >= CONN_CORO_ASYNC_AWAIT_READ && events <= CONN_CORO_ASYNC_AWAIT_READ_WRITE" , "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-request.c" , 2011, __extension__ __PRETTY_FUNCTION__); })); | |||
2012 | ||||
2013 | return (void)coro_yield(coro, make_async_yield_value(fd, events)); | |||
2014 | } | |||
2015 | ||||
2016 | void lwan_request_await_read(struct lwan_request *r, int fd) | |||
2017 | { | |||
2018 | return async_await_fd(r->conn->coro, fd, CONN_CORO_ASYNC_AWAIT_READ); | |||
2019 | } | |||
2020 | ||||
2021 | void lwan_request_await_write(struct lwan_request *r, int fd) | |||
2022 | { | |||
2023 | return async_await_fd(r->conn->coro, fd, CONN_CORO_ASYNC_AWAIT_WRITE); | |||
2024 | } | |||
2025 | ||||
2026 | void lwan_request_await_read_write(struct lwan_request *r, int fd) | |||
2027 | { | |||
2028 | return async_await_fd(r->conn->coro, fd, CONN_CORO_ASYNC_AWAIT_READ_WRITE); | |||
2029 | } | |||
2030 | ||||
2031 | ssize_t lwan_request_async_read_flags( | |||
2032 | struct lwan_request *request, int fd, void *buf, size_t len, int flags) | |||
2033 | { | |||
2034 | while (true1) { | |||
2035 | ssize_t r = recv(fd, buf, len, MSG_DONTWAITMSG_DONTWAIT | MSG_NOSIGNALMSG_NOSIGNAL | flags); | |||
2036 | ||||
2037 | if (r < 0) { | |||
2038 | switch (errno(*__errno_location ())) { | |||
2039 | case EWOULDBLOCK11: | |||
2040 | lwan_request_await_read(request, fd); | |||
2041 | /* Fallthrough */ | |||
2042 | case EINTR4: | |||
2043 | continue; | |||
2044 | case EPIPE32: | |||
2045 | return -errno(*__errno_location ()); | |||
2046 | } | |||
2047 | } | |||
2048 | ||||
2049 | return r; | |||
2050 | } | |||
2051 | } | |||
2052 | ||||
2053 | ssize_t lwan_request_async_read(struct lwan_request *request, | |||
2054 | int fd, | |||
2055 | void *buf, | |||
2056 | size_t len) | |||
2057 | { | |||
2058 | return lwan_request_async_read_flags(request, fd, buf, len, 0); | |||
2059 | } | |||
2060 | ||||
2061 | ssize_t lwan_request_async_write(struct lwan_request *request, | |||
2062 | int fd, | |||
2063 | const void *buf, | |||
2064 | size_t len) | |||
2065 | { | |||
2066 | while (true1) { | |||
2067 | ssize_t r = send(fd, buf, len, MSG_DONTWAITMSG_DONTWAIT|MSG_NOSIGNALMSG_NOSIGNAL); | |||
2068 | ||||
2069 | if (r < 0) { | |||
2070 | switch (errno(*__errno_location ())) { | |||
2071 | case EWOULDBLOCK11: | |||
2072 | lwan_request_await_write(request, fd); | |||
2073 | /* Fallthrough */ | |||
2074 | case EINTR4: | |||
2075 | continue; | |||
2076 | case EPIPE32: | |||
2077 | return -errno(*__errno_location ()); | |||
2078 | } | |||
2079 | } | |||
2080 | ||||
2081 | return r; | |||
2082 | } | |||
2083 | } | |||
2084 | ||||
2085 | ssize_t lwan_request_async_writev(struct lwan_request *request, | |||
2086 | int fd, | |||
2087 | struct iovec *iov, | |||
2088 | int iov_count) | |||
2089 | { | |||
2090 | ssize_t total_written = 0; | |||
2091 | int curr_iov = 0; | |||
2092 | ||||
2093 | for (int tries = 10; tries;) { | |||
2094 | const int remaining_len = (int)(iov_count - curr_iov); | |||
2095 | ssize_t written; | |||
2096 | ||||
2097 | if (remaining_len == 1) { | |||
2098 | const struct iovec *vec = &iov[curr_iov]; | |||
2099 | return lwan_request_async_write(request, fd, vec->iov_base, | |||
2100 | vec->iov_len); | |||
2101 | } | |||
2102 | ||||
2103 | written = writev(fd, iov + curr_iov, (size_t)remaining_len); | |||
2104 | if (UNLIKELY(written < 0)__builtin_expect(((written < 0)), (0))) { | |||
2105 | /* FIXME: Consider short writes as another try as well? */ | |||
2106 | tries--; | |||
2107 | ||||
2108 | switch (errno(*__errno_location ())) { | |||
2109 | case EAGAIN11: | |||
2110 | case EINTR4: | |||
2111 | goto try_again; | |||
2112 | default: | |||
2113 | goto out; | |||
2114 | } | |||
2115 | } | |||
2116 | ||||
2117 | total_written += written; | |||
2118 | ||||
2119 | while (curr_iov < iov_count && | |||
2120 | written >= (ssize_t)iov[curr_iov].iov_len) { | |||
2121 | written -= (ssize_t)iov[curr_iov].iov_len; | |||
2122 | curr_iov++; | |||
2123 | } | |||
2124 | ||||
2125 | if (curr_iov == iov_count) | |||
2126 | return total_written; | |||
2127 | ||||
2128 | iov[curr_iov].iov_base = (char *)iov[curr_iov].iov_base + written; | |||
2129 | iov[curr_iov].iov_len -= (size_t)written; | |||
2130 | ||||
2131 | try_again: | |||
2132 | lwan_request_await_write(request, fd); | |||
2133 | } | |||
2134 | ||||
2135 | out: | |||
2136 | coro_yield(request->conn->coro, CONN_CORO_ABORT); | |||
2137 | __builtin_unreachable(); | |||
2138 | } | |||
2139 | ||||
2140 | void lwan_request_foreach_header_for_cgi(struct lwan_request *request, | |||
2141 | void (*cb)(const char *header_name, | |||
2142 | size_t header_len, | |||
2143 | const char *value, | |||
2144 | size_t value_len, | |||
2145 | void *user_data), | |||
2146 | void *user_data) | |||
2147 | { | |||
2148 | struct lwan_request_parser_helper *helper = request->helper; | |||
2149 | char **header_start = helper->header_start; | |||
2150 | size_t n_header_start = helper->n_header_start; | |||
2151 | ||||
2152 | for (size_t i = 0; i < n_header_start; i++) { | |||
2153 | const char *header = header_start[i]; | |||
2154 | const char *next_header = header_start[i + 1]; | |||
2155 | const char *colon = memchr(header, ':', 127 - sizeof("HTTP_: ") - 1); | |||
2156 | char header_name[128]; | |||
2157 | int r; | |||
2158 | ||||
2159 | if (!colon) | |||
2160 | continue; | |||
2161 | ||||
2162 | const size_t header_len = (size_t)(colon - header); | |||
2163 | const size_t value_len = (size_t)(next_header - colon - 4); | |||
2164 | ||||
2165 | r = snprintf(header_name, sizeof(header_name), "HTTP_%.*s", | |||
2166 | (int)header_len, header); | |||
2167 | if (r < 0 || r >= (int)sizeof(header_name)) | |||
2168 | continue; | |||
2169 | ||||
2170 | /* FIXME: RFC7230/RFC3875 compliance */ | |||
2171 | for (char *p = header_name; *p; p++) { | |||
2172 | if (isalpha(*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int) _ISalpha)) | |||
2173 | *p &= ~0x20; | |||
2174 | else if (!isdigit(*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int) _ISdigit)) | |||
2175 | *p = '_'; | |||
2176 | } | |||
2177 | ||||
2178 | if (streq(header_name, "HTTP_PROXY")) { | |||
2179 | /* Mitigation for https://httpoxy.org */ | |||
2180 | continue; | |||
2181 | } | |||
2182 | ||||
2183 | cb(header_name, header_len + sizeof("HTTP_") - 1, colon + 2, value_len, | |||
2184 | user_data); | |||
2185 | } | |||
2186 | } |