1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26 #include "lextable-strings.h"
27
28
29 const unsigned char *
lws_token_to_string(enum lws_token_indexes token)30 lws_token_to_string(enum lws_token_indexes token)
31 {
32 if ((unsigned int)token >= LWS_ARRAY_SIZE(set))
33 return NULL;
34
35 return (unsigned char *)set[token];
36 }
37
38 /*
39 * Return http header index if one matches slen chars of s, or -1
40 */
41
42 int
lws_http_string_to_known_header(const char * s,size_t slen)43 lws_http_string_to_known_header(const char *s, size_t slen)
44 {
45 int n;
46
47 for (n = 0; n < (int)LWS_ARRAY_SIZE(set); n++)
48 if (!strncmp(set[n], s, slen))
49 return n;
50
51 return LWS_HTTP_NO_KNOWN_HEADER;
52 }
53
54 #ifdef LWS_WITH_HTTP2
55 int
lws_wsi_is_h2(struct lws * wsi)56 lws_wsi_is_h2(struct lws *wsi)
57 {
58 return wsi->upgraded_to_http2 ||
59 wsi->mux_substream ||
60 #if defined(LWS_WITH_CLIENT)
61 wsi->client_mux_substream ||
62 #endif
63 lwsi_role_h2(wsi) ||
64 lwsi_role_h2_ENCAPSULATION(wsi);
65 }
66 #endif
67
68 int
lws_add_http_header_by_name(struct lws * wsi,const unsigned char * name,const unsigned char * value,int length,unsigned char ** p,unsigned char * end)69 lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
70 const unsigned char *value, int length,
71 unsigned char **p, unsigned char *end)
72 {
73 #ifdef LWS_WITH_HTTP2
74 if (lws_wsi_is_h2(wsi))
75 return lws_add_http2_header_by_name(wsi, name,
76 value, length, p, end);
77 #else
78 (void)wsi;
79 #endif
80 if (name) {
81 while (*p < end && *name)
82 *((*p)++) = *name++;
83 if (*p == end)
84 return 1;
85 *((*p)++) = ' ';
86 }
87 if (*p + length + 3 >= end)
88 return 1;
89
90 if (value)
91 memcpy(*p, value, (unsigned int)length);
92 *p += length;
93 *((*p)++) = '\x0d';
94 *((*p)++) = '\x0a';
95
96 return 0;
97 }
98
lws_finalize_http_header(struct lws * wsi,unsigned char ** p,unsigned char * end)99 int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
100 unsigned char *end)
101 {
102 #ifdef LWS_WITH_HTTP2
103 if (lws_wsi_is_h2(wsi))
104 return 0;
105 #else
106 (void)wsi;
107 #endif
108 if ((lws_intptr_t)(end - *p) < 3)
109 return 1;
110 *((*p)++) = '\x0d';
111 *((*p)++) = '\x0a';
112
113 return 0;
114 }
115
116 int
lws_finalize_write_http_header(struct lws * wsi,unsigned char * start,unsigned char ** pp,unsigned char * end)117 lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
118 unsigned char **pp, unsigned char *end)
119 {
120 unsigned char *p;
121 int len;
122
123 if (lws_finalize_http_header(wsi, pp, end))
124 return 1;
125
126 p = *pp;
127 len = lws_ptr_diff(p, start);
128
129 if (lws_write(wsi, start, (unsigned int)len, LWS_WRITE_HTTP_HEADERS) != len)
130 return 1;
131
132 return 0;
133 }
134
135 int
lws_add_http_header_by_token(struct lws * wsi,enum lws_token_indexes token,const unsigned char * value,int length,unsigned char ** p,unsigned char * end)136 lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
137 const unsigned char *value, int length,
138 unsigned char **p, unsigned char *end)
139 {
140 const unsigned char *name;
141 #ifdef LWS_WITH_HTTP2
142 if (lws_wsi_is_h2(wsi))
143 return lws_add_http2_header_by_token(wsi, token, value,
144 length, p, end);
145 #endif
146 name = lws_token_to_string(token);
147 if (!name)
148 return 1;
149
150 return lws_add_http_header_by_name(wsi, name, value, length, p, end);
151 }
152
153 int
lws_add_http_header_content_length(struct lws * wsi,lws_filepos_t content_length,unsigned char ** p,unsigned char * end)154 lws_add_http_header_content_length(struct lws *wsi,
155 lws_filepos_t content_length,
156 unsigned char **p, unsigned char *end)
157 {
158 char b[24];
159 int n;
160
161 n = lws_snprintf(b, sizeof(b) - 1, "%llu", (unsigned long long)content_length);
162 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
163 (unsigned char *)b, n, p, end))
164 return 1;
165 wsi->http.tx_content_length = content_length;
166 wsi->http.tx_content_remain = content_length;
167
168 lwsl_info("%s: %s: tx_content_length/remain %llu\n", __func__,
169 lws_wsi_tag(wsi), (unsigned long long)content_length);
170
171 return 0;
172 }
173
174 #if defined(LWS_WITH_SERVER)
175
176 int
lws_add_http_common_headers(struct lws * wsi,unsigned int code,const char * content_type,lws_filepos_t content_len,unsigned char ** p,unsigned char * end)177 lws_add_http_common_headers(struct lws *wsi, unsigned int code,
178 const char *content_type, lws_filepos_t content_len,
179 unsigned char **p, unsigned char *end)
180 {
181 const char *ka[] = { "close", "keep-alive" };
182 int types[] = { HTTP_CONNECTION_CLOSE, HTTP_CONNECTION_KEEP_ALIVE },
183 t = 0;
184
185 if (lws_add_http_header_status(wsi, code, p, end))
186 return 1;
187
188 if (content_type &&
189 lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
190 (unsigned char *)content_type,
191 (int)strlen(content_type), p, end))
192 return 1;
193
194 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
195 if (!wsi->http.lcs && content_type &&
196 (!strncmp(content_type, "text/", 5) ||
197 !strcmp(content_type, "application/javascript") ||
198 !strcmp(content_type, "image/svg+xml")))
199 lws_http_compression_apply(wsi, NULL, p, end, 0);
200 #endif
201
202 /*
203 * if we decided to compress it, we don't know the content length...
204 * the compressed data will go out chunked on h1
205 */
206 if (
207 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
208 !wsi->http.lcs &&
209 #endif
210 content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN) {
211 if (lws_add_http_header_content_length(wsi, content_len,
212 p, end))
213 return 1;
214 } else {
215 /* there was no length... it normally means CONNECTION_CLOSE */
216 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
217
218 if (!wsi->mux_substream && wsi->http.lcs) {
219 /* so...
220 * - h1 connection
221 * - http compression transform active
222 * - did not send content length
223 *
224 * then mark as chunked...
225 */
226 wsi->http.comp_ctx.chunking = 1;
227 if (lws_add_http_header_by_token(wsi,
228 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
229 (unsigned char *)"chunked", 7, p, end))
230 return -1;
231
232 /* ... but h1 compression is chunked, if active we can
233 * still pipeline
234 */
235 if (wsi->http.lcs &&
236 wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
237 t = 1;
238 }
239 #endif
240 if (!wsi->mux_substream) {
241 if (lws_add_http_header_by_token(wsi,
242 WSI_TOKEN_CONNECTION,
243 (unsigned char *)ka[t],
244 (int)strlen(ka[t]), p, end))
245 return 1;
246
247 wsi->http.conn_type = (enum http_conn_type)types[t];
248 }
249 }
250
251 return 0;
252 }
253
254 static const char * const err400[] = {
255 "Bad Request",
256 "Unauthorized",
257 "Payment Required",
258 "Forbidden",
259 "Not Found",
260 "Method Not Allowed",
261 "Not Acceptable",
262 "Proxy Auth Required",
263 "Request Timeout",
264 "Conflict",
265 "Gone",
266 "Length Required",
267 "Precondition Failed",
268 "Request Entity Too Large",
269 "Request URI too Long",
270 "Unsupported Media Type",
271 "Requested Range Not Satisfiable",
272 "Expectation Failed"
273 };
274
275 static const char * const err500[] = {
276 "Internal Server Error",
277 "Not Implemented",
278 "Bad Gateway",
279 "Service Unavailable",
280 "Gateway Timeout",
281 "HTTP Version Not Supported"
282 };
283
284 /* security best practices from Mozilla Observatory */
285
286 static const
287 struct lws_protocol_vhost_options pvo_hsbph[] = {{
288 NULL, NULL, "referrer-policy:", "no-referrer"
289 }, {
290 &pvo_hsbph[0], NULL, "x-frame-options:", "deny"
291 }, {
292 &pvo_hsbph[1], NULL, "x-xss-protection:", "1; mode=block"
293 }, {
294 &pvo_hsbph[2], NULL, "x-content-type-options:", "nosniff"
295 }, {
296 &pvo_hsbph[3], NULL, "content-security-policy:",
297 "default-src 'none'; img-src 'self' data: ; "
298 "script-src 'self'; font-src 'self'; "
299 "style-src 'self'; connect-src 'self' ws: wss:; "
300 "frame-ancestors 'none'; base-uri 'none';"
301 "form-action 'self';"
302 }};
303
304 int
lws_add_http_header_status(struct lws * wsi,unsigned int _code,unsigned char ** p,unsigned char * end)305 lws_add_http_header_status(struct lws *wsi, unsigned int _code,
306 unsigned char **p, unsigned char *end)
307 {
308 static const char * const hver[] = {
309 "HTTP/1.0", "HTTP/1.1", "HTTP/2"
310 };
311 const struct lws_protocol_vhost_options *headers;
312 unsigned int code = _code & LWSAHH_CODE_MASK;
313 const char *description = "", *p1;
314 unsigned char code_and_desc[60];
315 int n;
316
317 wsi->http.response_code = code;
318 #ifdef LWS_WITH_ACCESS_LOG
319 wsi->http.access_log.response = (int)code;
320 #endif
321
322 #ifdef LWS_WITH_HTTP2
323 if (lws_wsi_is_h2(wsi)) {
324 n = lws_add_http2_header_status(wsi, code, p, end);
325 if (n)
326 return n;
327 } else
328 #endif
329 {
330 if (code >= 400 && code < (400 + LWS_ARRAY_SIZE(err400)))
331 description = err400[code - 400];
332 if (code >= 500 && code < (500 + LWS_ARRAY_SIZE(err500)))
333 description = err500[code - 500];
334
335 if (code == 100)
336 description = "Continue";
337 if (code == 200)
338 description = "OK";
339 if (code == 304)
340 description = "Not Modified";
341 else
342 if (code >= 300 && code < 400)
343 description = "Redirect";
344
345 if (wsi->http.request_version < LWS_ARRAY_SIZE(hver))
346 p1 = hver[wsi->http.request_version];
347 else
348 p1 = hver[0];
349
350 n = lws_snprintf((char *)code_and_desc,
351 sizeof(code_and_desc) - 1, "%s %u %s",
352 p1, code, description);
353
354 if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p,
355 end))
356 return 1;
357 }
358
359 headers = wsi->a.vhost->headers;
360 while (headers) {
361 if (lws_add_http_header_by_name(wsi,
362 (const unsigned char *)headers->name,
363 (unsigned char *)headers->value,
364 (int)strlen(headers->value), p, end))
365 return 1;
366
367 headers = headers->next;
368 }
369
370 if (wsi->a.vhost->options &
371 LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE) {
372 headers = &pvo_hsbph[LWS_ARRAY_SIZE(pvo_hsbph) - 1];
373 while (headers) {
374 if (lws_add_http_header_by_name(wsi,
375 (const unsigned char *)headers->name,
376 (unsigned char *)headers->value,
377 (int)strlen(headers->value), p, end))
378 return 1;
379
380 headers = headers->next;
381 }
382 }
383
384 if (wsi->a.context->server_string &&
385 !(_code & LWSAHH_FLAG_NO_SERVER_NAME)) {
386 assert(wsi->a.context->server_string_len > 0);
387 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
388 (unsigned char *)wsi->a.context->server_string,
389 wsi->a.context->server_string_len, p, end))
390 return 1;
391 }
392
393 if (wsi->a.vhost->options & LWS_SERVER_OPTION_STS)
394 if (lws_add_http_header_by_name(wsi, (unsigned char *)
395 "Strict-Transport-Security:",
396 (unsigned char *)"max-age=15768000 ; "
397 "includeSubDomains", 36, p, end))
398 return 1;
399
400 if (*p >= (end - 2)) {
401 lwsl_err("%s: reached end of buffer\n", __func__);
402
403 return 1;
404 }
405
406 return 0;
407 }
408
409 int
lws_return_http_status(struct lws * wsi,unsigned int code,const char * html_body)410 lws_return_http_status(struct lws *wsi, unsigned int code,
411 const char *html_body)
412 {
413 struct lws_context *context = lws_get_context(wsi);
414 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
415 unsigned char *p = pt->serv_buf + LWS_PRE;
416 unsigned char *start = p;
417 unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
418 char *body = (char *)start + context->pt_serv_buf_size - 512;
419 int n = 0, m = 0, len;
420 char slen[20];
421
422 if (!wsi->a.vhost) {
423 lwsl_err("%s: wsi not bound to vhost\n", __func__);
424
425 return 1;
426 }
427 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
428 if (!wsi->handling_404 &&
429 wsi->a.vhost->http.error_document_404 &&
430 code == HTTP_STATUS_NOT_FOUND)
431 /* we should do a redirect, and do the 404 there */
432 if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
433 (uint8_t *)wsi->a.vhost->http.error_document_404,
434 (int)strlen(wsi->a.vhost->http.error_document_404),
435 &p, end) > 0)
436 return 0;
437 #endif
438
439 /* if the redirect failed, just do a simple status */
440 p = start;
441
442 if (!html_body)
443 html_body = "";
444
445 if (lws_add_http_header_status(wsi, code, &p, end))
446 return 1;
447
448 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
449 (unsigned char *)"text/html", 9,
450 &p, end))
451 return 1;
452
453 len = lws_snprintf(body, 510, "<html><head>"
454 "<meta charset=utf-8 http-equiv=\"Content-Language\" "
455 "content=\"en\"/>"
456 "<link rel=\"stylesheet\" type=\"text/css\" "
457 "href=\"/error.css\"/>"
458 "</head><body><h1>%u</h1>%s</body></html>", code, html_body);
459
460
461 n = lws_snprintf(slen, 12, "%d", len);
462 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
463 (unsigned char *)slen, n, &p, end))
464 return 1;
465
466 if (lws_finalize_http_header(wsi, &p, end))
467 return 1;
468
469 #if defined(LWS_WITH_HTTP2)
470 if (wsi->mux_substream) {
471
472 /*
473 * for HTTP/2, the headers must be sent separately, since they
474 * go out in their own frame. That puts us in a bind that
475 * we won't always be able to get away with two lws_write()s in
476 * sequence, since the first may use up the writability due to
477 * the pipe being choked or SSL_WANT_.
478 *
479 * However we do need to send the human-readable body, and the
480 * END_STREAM.
481 *
482 * Solve it by writing the headers now...
483 */
484 m = lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
485 LWS_WRITE_HTTP_HEADERS);
486 if (m != lws_ptr_diff(p, start))
487 return 1;
488
489 /*
490 * ... but stash the body and send it as a priority next
491 * handle_POLLOUT
492 */
493 wsi->http.tx_content_length = (unsigned int)len;
494 wsi->http.tx_content_remain = (unsigned int)len;
495
496 wsi->h2.pending_status_body = lws_malloc((unsigned int)len + LWS_PRE + 1,
497 "pending status body");
498 if (!wsi->h2.pending_status_body)
499 return -1;
500
501 strcpy(wsi->h2.pending_status_body + LWS_PRE, body);
502 lws_callback_on_writable(wsi);
503
504 return 0;
505 } else
506 #endif
507 {
508 /*
509 * for http/1, we can just append the body after the finalized
510 * headers and send it all in one go.
511 */
512
513 n = lws_ptr_diff(p, start) + len;
514 memcpy(p, body, (unsigned int)len);
515 m = lws_write(wsi, start, (unsigned int)n, LWS_WRITE_HTTP);
516 if (m != n)
517 return 1;
518 }
519
520 return m != n;
521 }
522
523 int
lws_http_redirect(struct lws * wsi,int code,const unsigned char * loc,int len,unsigned char ** p,unsigned char * end)524 lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
525 unsigned char **p, unsigned char *end)
526 {
527 unsigned char *start = *p;
528
529 if (lws_add_http_header_status(wsi, (unsigned int)code, p, end))
530 return -1;
531
532 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
533 p, end))
534 return -1;
535 /*
536 * if we're going with http/1.1 and keepalive, we have to give fake
537 * content metadata so the client knows we completed the transaction and
538 * it can do the redirect...
539 */
540 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
541 (unsigned char *)"text/html", 9, p,
542 end))
543 return -1;
544 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
545 (unsigned char *)"0", 1, p, end))
546 return -1;
547
548 if (lws_finalize_http_header(wsi, p, end))
549 return -1;
550
551 return lws_write(wsi, start, lws_ptr_diff_size_t(*p, start),
552 LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
553 }
554 #endif
555
556 #if !defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
557 int
lws_http_compression_apply(struct lws * wsi,const char * name,unsigned char ** p,unsigned char * end,char decomp)558 lws_http_compression_apply(struct lws *wsi, const char *name,
559 unsigned char **p, unsigned char *end, char decomp)
560 {
561 (void)wsi;
562 (void)name;
563 (void)p;
564 (void)end;
565 (void)decomp;
566
567 return 0;
568 }
569 #endif
570
571 int
lws_http_headers_detach(struct lws * wsi)572 lws_http_headers_detach(struct lws *wsi)
573 {
574 return lws_header_table_detach(wsi, 0);
575 }
576
577 #if defined(LWS_WITH_SERVER)
578
579 void
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t * sul)580 lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul)
581 {
582 struct allocated_headers *ah;
583 struct lws_context_per_thread *pt = lws_container_of(sul,
584 struct lws_context_per_thread, sul_ah_lifecheck);
585 struct lws *wsi;
586 time_t now;
587 int m;
588
589 now = time(NULL);
590
591 lws_pt_lock(pt, __func__);
592
593 ah = pt->http.ah_list;
594 while (ah) {
595 int len;
596 char buf[256];
597 const unsigned char *c;
598
599 if (!ah->in_use || !ah->wsi || !ah->assigned ||
600 (ah->wsi->a.vhost &&
601 (now - ah->assigned) <
602 ah->wsi->a.vhost->timeout_secs_ah_idle + 360)) {
603 ah = ah->next;
604 continue;
605 }
606
607 /*
608 * a single ah session somehow got held for
609 * an unreasonable amount of time.
610 *
611 * Dump info on the connection...
612 */
613 wsi = ah->wsi;
614 buf[0] = '\0';
615 #if !defined(LWS_PLAT_OPTEE)
616 lws_get_peer_simple(wsi, buf, sizeof(buf));
617 #else
618 buf[0] = '\0';
619 #endif
620 lwsl_notice("%s: ah excessive hold: wsi %p\n"
621 " peer address: %s\n"
622 " ah pos %lu\n", __func__, lws_wsi_tag(wsi),
623 buf, (unsigned long)ah->pos);
624 buf[0] = '\0';
625 m = 0;
626 do {
627 c = lws_token_to_string((enum lws_token_indexes)m);
628 if (!c)
629 break;
630 if (!(*c))
631 break;
632
633 len = lws_hdr_total_length(wsi, (enum lws_token_indexes)m);
634 if (!len || len > (int)sizeof(buf) - 1) {
635 m++;
636 continue;
637 }
638
639 if (lws_hdr_copy(wsi, buf, sizeof buf, (enum lws_token_indexes)m) > 0) {
640 buf[sizeof(buf) - 1] = '\0';
641
642 lwsl_notice(" %s = %s\n",
643 (const char *)c, buf);
644 }
645 m++;
646 } while (1);
647
648 /* explicitly detach the ah */
649 lws_header_table_detach(wsi, 0);
650
651 /* ... and then drop the connection */
652
653 __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
654 "excessive ah");
655
656 ah = pt->http.ah_list;
657 }
658
659 lws_pt_unlock(pt);
660 }
661 #endif
662