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