• 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 
27 #if !defined(SOL_TCP) && defined(IPPROTO_TCP)
28 #define SOL_TCP IPPROTO_TCP
29 #endif
30 
31 const char * const method_names[] = {
32 	"GET", "POST",
33 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
34 	"OPTIONS", "PUT", "PATCH", "DELETE",
35 #endif
36 	"CONNECT", "HEAD",
37 #ifdef LWS_WITH_HTTP2
38 	":path",
39 #endif
40 	};
41 
42 #if defined(LWS_WITH_FILE_OPS)
43 static const char * const intermediates[] = { "private", "public" };
44 #endif
45 
46 /*
47  * return 0: all done
48  *        1: nonfatal error
49  *       <0: fatal error
50  *
51  *       REQUIRES CONTEXT LOCK HELD
52  */
53 
54 #if defined(LWS_WITH_SERVER)
55 
56 struct vh_sock_args {
57 	const struct lws_context_creation_info	*info;
58 	struct lws_vhost			*vhost;
59 	int					af;
60 };
61 
62 
63 static int
check_extant(struct lws_dll2 * d,void * user)64 check_extant(struct lws_dll2 *d, void *user)
65 {
66 	struct lws *wsi = lws_container_of(d, struct lws, listen_list);
67 	struct vh_sock_args *a = (struct vh_sock_args *)user;
68 
69 	if (!lws_vhost_compare_listen(wsi->a.vhost, a->vhost))
70 		return 0;
71 
72 	if (wsi->af != a ->af)
73 		return 0;
74 
75 	lwsl_notice(" using listen skt from vhost %s\n", wsi->a.vhost->name);
76 
77 	return 1;
78 }
79 
80 /*
81  * Creates a single listen socket of a specific AF
82  */
83 
84 int
_lws_vhost_init_server_af(struct vh_sock_args * a)85 _lws_vhost_init_server_af(struct vh_sock_args *a)
86 {
87 	struct lws_context *cx = a->vhost->context;
88 	struct lws_context_per_thread *pt;
89 	int n, opt = 1, limit = 1;
90 	lws_sockfd_type sockfd;
91 	struct lws *wsi;
92 	int m = 0, is;
93 #if defined(LWS_WITH_IPV6)
94 	int value = 1;
95 #endif
96 
97 	(void)method_names;
98 	(void)opt;
99 
100 	lwsl_info("%s: af %d\n", __func__, (int)a->af);
101 
102 	if (lws_vhost_foreach_listen_wsi(a->vhost->context, a, check_extant))
103 		return 0;
104 
105 deal:
106 
107 	if (a->vhost->iface) {
108 
109 		/*
110 		 * let's check before we do anything else about the disposition
111 		 * of the interface he wants to bind to...
112 		 */
113 		is = lws_socket_bind(a->vhost, NULL, LWS_SOCK_INVALID,
114 				     a->vhost->listen_port, a->vhost->iface,
115 				     a->af);
116 		lwsl_debug("initial if check says %d\n", is);
117 
118 		if (is == LWS_ITOSA_BUSY)
119 			/* treat as fatal */
120 			return -1;
121 
122 		lws_start_foreach_llp(struct lws_vhost **, pv,
123 				      cx->no_listener_vhost_list) {
124 			if (is >= LWS_ITOSA_USABLE && *pv == a->vhost) {
125 				/* on the list and shouldn't be: remove it */
126 				lwsl_debug("deferred iface: removing vh %s\n",
127 						(*pv)->name);
128 				*pv = a->vhost->no_listener_vhost_list;
129 				a->vhost->no_listener_vhost_list = NULL;
130 				goto done_list;
131 			}
132 			if (is < LWS_ITOSA_USABLE && *pv == a->vhost)
133 				goto done_list;
134 		} lws_end_foreach_llp(pv, no_listener_vhost_list);
135 
136 		/* not on the list... */
137 
138 		if (is < LWS_ITOSA_USABLE) {
139 
140 			/* ... but needs to be: so add it */
141 
142 			lwsl_debug("deferred iface: adding vh %s\n",
143 					a->vhost->name);
144 			a->vhost->no_listener_vhost_list =
145 					cx->no_listener_vhost_list;
146 			cx->no_listener_vhost_list = a->vhost;
147 		}
148 
149 done_list:
150 
151 		switch (is) {
152 		default:
153 			break;
154 		case LWS_ITOSA_NOT_EXIST:
155 			/* can't add it */
156 			if (!a->info)
157 				return -1;
158 
159 			/* first time */
160 			lwsl_err("%s: VH %s: iface %s port %d DOESN'T EXIST\n",
161 				 __func__, a->vhost->name, a->vhost->iface,
162 				 a->vhost->listen_port);
163 
164 			return (a->info->options &
165 				LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) ==
166 				LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND ?
167 				-1 : 1;
168 
169 		case LWS_ITOSA_NOT_USABLE:
170 			/* can't add it */
171 			if (!a->info) /* first time */
172 				return -1;
173 
174 			lwsl_err("%s: VH %s: iface %s port %d NOT USABLE\n",
175 				 __func__, a->vhost->name, a->vhost->iface,
176 				 a->vhost->listen_port);
177 
178 			return (a->info->options &
179 				LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) ==
180 				LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND ?
181 				-1 : 1;
182 		}
183 	}
184 
185 	(void)n;
186 #if defined(__linux__)
187 	/*
188 	 * A Unix domain sockets cannot be bound multiple times, even if we
189 	 * set the SO_REUSE* options on.
190 	 *
191 	 * However on recent linux, each thread is able to independently listen.
192 	 *
193 	 * So we can assume creating just one listening socket for a multi-
194 	 * threaded environment will typically work.
195 	 */
196 	if (a->af != AF_UNIX)
197 		limit = cx->count_threads;
198 #endif
199 
200 	for (m = 0; m < limit; m++) {
201 
202 		sockfd = lws_fi(&a->vhost->fic, "listenskt") ?
203 					LWS_SOCK_INVALID :
204 					socket(a->af, SOCK_STREAM, 0);
205 
206 		if (sockfd == LWS_SOCK_INVALID) {
207 			lwsl_err("ERROR opening socket\n");
208 			return 1;
209 		}
210 
211 #if !defined(LWS_PLAT_FREERTOS)
212 #if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
213 		/*
214 		 * only accept that we are the only listener on the port
215 		 * https://msdn.microsoft.com/zh-tw/library/
216 		 *    windows/desktop/ms740621(v=vs.85).aspx
217 		 *
218 		 * for lws, to match Linux, we default to exclusive listen
219 		 */
220 		if (!lws_check_opt(a->vhost->options,
221 				LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
222 			if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
223 				       (const void *)&opt, sizeof(opt)) < 0) {
224 				lwsl_err("reuseaddr failed\n");
225 				compatible_close(sockfd);
226 				return -1;
227 			}
228 		} else
229 #endif
230 
231 		/*
232 		 * allow us to restart even if old sockets in TIME_WAIT
233 		 */
234 		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
235 			       (const void *)&opt, sizeof(opt)) < 0) {
236 			lwsl_err("reuseaddr failed\n");
237 			compatible_close(sockfd);
238 			return -1;
239 		}
240 
241 #if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY)
242 		/*
243 		 * If we have an ipv6 listen socket, it only accepts ipv6.
244 		 *
245 		 * There will be a separate ipv4 listen socket if that's
246 		 * enabled.
247 		 */
248 		if (a->af == AF_INET6 &&
249 		    setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
250 			       (const void*)&value, sizeof(value)) < 0) {
251 			compatible_close(sockfd);
252 			return -1;
253 		}
254 #endif
255 
256 #if defined(__linux__) && defined(SO_REUSEPORT)
257 		/* keep coverity happy */
258 #if LWS_MAX_SMP > 1
259 		n = 1;
260 #else
261 		n = lws_check_opt(a->vhost->options,
262 				  LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
263 #endif
264 		if (n || cx->count_threads > 1) /* ... also implied by threads > 1 */
265 			if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
266 					(const void *)&opt, sizeof(opt)) < 0) {
267 				compatible_close(sockfd);
268 				return -1;
269 			}
270 #endif
271 #endif
272 		lws_plat_set_socket_options(a->vhost, sockfd, 0);
273 
274 		is = lws_socket_bind(a->vhost, NULL, sockfd,
275 				     a->vhost->listen_port,
276 				     a->vhost->iface, a->af);
277 
278 		if (is == LWS_ITOSA_BUSY) {
279 			/* treat as fatal */
280 			compatible_close(sockfd);
281 
282 			return -1;
283 		}
284 
285 		/*
286 		 * There is a race where the network device may come up and then
287 		 * go away and fail here.  So correctly handle unexpected failure
288 		 * here despite we earlier confirmed it.
289 		 */
290 		if (is < 0) {
291 			lwsl_info("%s: lws_socket_bind says %d\n", __func__, is);
292 			compatible_close(sockfd);
293 			if (a->vhost->iface)
294 				goto deal;
295 			return -1;
296 		}
297 
298 		/*
299 		 * Create the listen wsi and customize it
300 		 */
301 
302 		lws_context_lock(cx, __func__);
303 		wsi = __lws_wsi_create_with_role(cx, m, &role_ops_listen, NULL);
304 		lws_context_unlock(cx);
305 		if (wsi == NULL) {
306 			lwsl_err("Out of mem\n");
307 			goto bail;
308 		}
309 
310 		wsi->af = (uint8_t)a->af;
311 
312 #ifdef LWS_WITH_UNIX_SOCK
313 		if (!LWS_UNIX_SOCK_ENABLED(a->vhost))
314 #endif
315 		{
316 			wsi->unix_skt = 1;
317 			a->vhost->listen_port = is;
318 
319 			lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
320 		}
321 
322 		wsi->desc.sockfd = sockfd;
323 		wsi->a.protocol = a->vhost->protocols;
324 		lws_vhost_bind_wsi(a->vhost, wsi);
325 		wsi->listener = 1;
326 
327 		if (wsi->a.context->event_loop_ops->init_vhost_listen_wsi)
328 			wsi->a.context->event_loop_ops->init_vhost_listen_wsi(wsi);
329 
330 		pt = &cx->pt[m];
331 		lws_pt_lock(pt, __func__);
332 
333 		if (__insert_wsi_socket_into_fds(cx, wsi)) {
334 			lwsl_notice("inserting wsi socket into fds failed\n");
335 			lws_pt_unlock(pt);
336 			goto bail;
337 		}
338 
339 		lws_dll2_add_tail(&wsi->listen_list, &a->vhost->listen_wsi);
340 		lws_pt_unlock(pt);
341 
342 #if defined(WIN32) && defined(TCP_FASTOPEN)
343 		if (a->vhost->fo_listen_queue) {
344 			int optval = 1;
345 			if (setsockopt(wsi->desc.sockfd, IPPROTO_TCP,
346 				       TCP_FASTOPEN,
347 				       (const char*)&optval, sizeof(optval)) < 0) {
348 				int error = LWS_ERRNO;
349 				lwsl_warn("%s: TCP_NODELAY failed with error %d\n",
350 						__func__, error);
351 			}
352 		}
353 #else
354 #if defined(TCP_FASTOPEN)
355 		if (a->vhost->fo_listen_queue) {
356 			int qlen = a->vhost->fo_listen_queue;
357 
358 			if (setsockopt(wsi->desc.sockfd, SOL_TCP, TCP_FASTOPEN,
359 				       &qlen, sizeof(qlen)))
360 				lwsl_warn("%s: TCP_FASTOPEN failed\n", __func__);
361 		}
362 #endif
363 #endif
364 
365 		n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
366 		if (n < 0) {
367 			lwsl_err("listen failed with error %d\n", LWS_ERRNO);
368 			lws_dll2_remove(&wsi->listen_list);
369 			__remove_wsi_socket_from_fds(wsi);
370 			goto bail;
371 		}
372 
373 		if (wsi)
374 			__lws_lc_tag(a->vhost->context,
375 				     &a->vhost->context->lcg[LWSLCG_WSI],
376 				     &wsi->lc, "listen|%s|%s|%d",
377 				     a->vhost->name,
378 				     a->vhost->iface ? a->vhost->iface : "",
379 				     (int)a->vhost->listen_port);
380 
381 	} /* for each thread able to independently listen */
382 
383 	if (!lws_check_opt(cx->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
384 #ifdef LWS_WITH_UNIX_SOCK
385 		if (a->af == AF_UNIX)
386 			lwsl_info(" Listening on \"%s\"\n", a->vhost->iface);
387 		else
388 #endif
389 			lwsl_info(" Listening on %s:%d\n",
390 					a->vhost->iface,
391 					a->vhost->listen_port);
392         }
393 
394 	// info->port = vhost->listen_port;
395 
396 	return 0;
397 
398 bail:
399 	compatible_close(sockfd);
400 
401 	return -1;
402 }
403 
404 
405 int
_lws_vhost_init_server(const struct lws_context_creation_info * info,struct lws_vhost * vhost)406 _lws_vhost_init_server(const struct lws_context_creation_info *info,
407 		       struct lws_vhost *vhost)
408 {
409 	struct vh_sock_args a;
410 
411 	a.info = info;
412 	a.vhost = vhost;
413 
414 	if (info) {
415 		vhost->iface = info->iface;
416 		vhost->listen_port = info->port;
417 	}
418 
419 	/* set up our external listening socket we serve on */
420 
421 	if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN ||
422 	    vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER)
423 		return 0;
424 
425 	/*
426 	 * Let's figure out what AF(s) we want this vhost to listen on.
427 	 *
428 	 * We want AF_UNIX alone if that's what's told
429 	 */
430 
431 #if defined(LWS_WITH_UNIX_SOCK)
432 	/*
433 	 * If unix socket, ask for that and we are done
434 	 */
435 	if (LWS_UNIX_SOCK_ENABLED(vhost)) {
436 		a.af = AF_UNIX;
437 		goto single;
438 	}
439 #endif
440 
441 	/*
442 	 * We may support both ipv4 and ipv6, but get a numeric vhost listen
443 	 * iface that is unambiguously ipv4 or ipv6, meaning we can only listen
444 	 * for the related AF then.
445 	 */
446 
447 	if (vhost->iface) {
448 		uint8_t buf[16];
449 		int q;
450 
451 		q = lws_parse_numeric_address(vhost->iface, buf, sizeof(buf));
452 
453 		if (q == 4) {
454 			a.af = AF_INET;
455 			goto single;
456 		}
457 
458 		if (q == 16) {
459 #if defined(LWS_WITH_IPV6)
460 			if (LWS_IPV6_ENABLED(vhost)) {
461 				a.af = AF_INET6;
462 				goto single;
463 			}
464 #endif
465 			lwsl_err("%s: ipv6 not supported on %s\n", __func__,
466 					vhost->name);
467 			return 1;
468 		}
469 	}
470 
471 	/*
472 	 * ... if we make it here, we would want to listen on AF_INET and
473 	 * AF_INET6 unless one or the other is forbidden
474 	 */
475 
476 #if defined(LWS_WITH_IPV6)
477 	if (!(LWS_IPV6_ENABLED(vhost) &&
478 	      (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) &&
479 	      (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE))) {
480 #endif
481 		a.af = AF_INET;
482 		if (_lws_vhost_init_server_af(&a))
483 			return 1;
484 
485 #if defined(LWS_WITH_IPV6)
486 	}
487 	if (LWS_IPV6_ENABLED(vhost)) {
488 		a.af = AF_INET6;
489 		goto single;
490 	}
491 #endif
492 
493 	return 0;
494 
495 single:
496 	return _lws_vhost_init_server_af(&a);
497 }
498 
499 #endif
500 
501 struct lws_vhost *
lws_select_vhost(struct lws_context * context,int port,const char * servername)502 lws_select_vhost(struct lws_context *context, int port, const char *servername)
503 {
504 	struct lws_vhost *vhost = context->vhost_list;
505 	const char *p;
506 	int n, colon;
507 
508 	n = (int)strlen(servername);
509 	colon = n;
510 	p = strchr(servername, ':');
511 	if (p)
512 		colon = lws_ptr_diff(p, servername);
513 
514 	/* Priotity 1: first try exact matches */
515 
516 	while (vhost) {
517 		if (port == vhost->listen_port &&
518 		    !strncmp(vhost->name, servername, (unsigned int)colon)) {
519 			lwsl_info("SNI: Found: %s\n", servername);
520 			return vhost;
521 		}
522 		vhost = vhost->vhost_next;
523 	}
524 
525 	/*
526 	 * Priority 2: if no exact matches, try matching *.vhost-name
527 	 * unintentional matches are possible but resolve to x.com for *.x.com
528 	 * which is reasonable.  If exact match exists we already chose it and
529 	 * never reach here.  SSL will still fail it if the cert doesn't allow
530 	 * *.x.com.
531 	 */
532 	vhost = context->vhost_list;
533 	while (vhost) {
534 		int m = (int)strlen(vhost->name);
535 		if (port && port == vhost->listen_port &&
536 		    m <= (colon - 2) &&
537 		    servername[colon - m - 1] == '.' &&
538 		    !strncmp(vhost->name, servername + colon - m, (unsigned int)m)) {
539 			lwsl_info("SNI: Found %s on wildcard: %s\n",
540 				    servername, vhost->name);
541 			return vhost;
542 		}
543 		vhost = vhost->vhost_next;
544 	}
545 
546 	/* Priority 3: match the first vhost on our port */
547 
548 	vhost = context->vhost_list;
549 	while (vhost) {
550 		if (port && port == vhost->listen_port) {
551 			lwsl_info("%s: vhost match to %s based on port %d\n",
552 					__func__, vhost->name, port);
553 			return vhost;
554 		}
555 		vhost = vhost->vhost_next;
556 	}
557 
558 	/* no match */
559 
560 	return NULL;
561 }
562 
563 static const struct lws_mimetype {
564 	const char *extension;
565 	const char *mimetype;
566 } server_mimetypes[] = {
567 	{ ".html", "text/html" },
568 	{ ".htm", "text/html" },
569 	{ ".js", "text/javascript" },
570 	{ ".css", "text/css" },
571 	{ ".png", "image/png" },
572 	{ ".jpg", "image/jpeg" },
573 	{ ".jpeg", "image/jpeg" },
574 	{ ".ico", "image/x-icon" },
575 	{ ".gif", "image/gif" },
576 	{ ".svg", "image/svg+xml" },
577 	{ ".ttf", "application/x-font-ttf" },
578 	{ ".otf", "application/font-woff" },
579 	{ ".woff", "application/font-woff" },
580 	{ ".woff2", "application/font-woff2" },
581 	{ ".gz", "application/gzip" },
582 	{ ".txt", "text/plain" },
583 	{ ".xml", "application/xml" },
584 	{ ".json", "application/json" },
585 	{ ".mjs", "text/javascript" },
586 };
587 
588 const char *
lws_get_mimetype(const char * file,const struct lws_http_mount * m)589 lws_get_mimetype(const char *file, const struct lws_http_mount *m)
590 {
591 	const struct lws_protocol_vhost_options *pvo;
592 	size_t n = strlen(file), len, i;
593 	const char *fallback_mimetype = NULL;
594 	const struct lws_mimetype *mt;
595 
596 	/* prioritize user-defined mimetypes */
597 	for (pvo = m ? m->extra_mimetypes : NULL; pvo; pvo = pvo->next) {
598 		/* ie, match anything */
599 		if (!fallback_mimetype && pvo->name[0] == '*') {
600 			fallback_mimetype = pvo->value;
601 			continue;
602 		}
603 
604 		len = strlen(pvo->name);
605 		if (n > len && !strcasecmp(&file[n - len], pvo->name)) {
606 			lwsl_info("%s: match to user mimetype: %s\n", __func__,
607 				  pvo->value);
608 			return pvo->value;
609 		}
610 	}
611 
612 	/* fallback to server-defined mimetypes */
613 	for (i = 0; i < LWS_ARRAY_SIZE(server_mimetypes); ++i) {
614 		mt = &server_mimetypes[i];
615 
616 		len = strlen(mt->extension);
617 		if (n > len && !strcasecmp(&file[n - len], mt->extension)) {
618 			lwsl_info("%s: match to server mimetype: %s\n", __func__,
619 				  mt->mimetype);
620 			return mt->mimetype;
621 		}
622 	}
623 
624 	/* fallback to '*' if defined */
625 	if (fallback_mimetype) {
626 		lwsl_info("%s: match to any mimetype: %s\n", __func__,
627 			  fallback_mimetype);
628 		return fallback_mimetype;
629 	}
630 
631 	return NULL;
632 }
633 
634 #if defined(LWS_WITH_FILE_OPS)
635 static lws_fop_flags_t
lws_vfs_prepare_flags(struct lws * wsi)636 lws_vfs_prepare_flags(struct lws *wsi)
637 {
638 	lws_fop_flags_t f = 0;
639 
640 	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
641 		return f;
642 
643 	if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
644 		   "gzip")) {
645 		lwsl_info("client indicates GZIP is acceptable\n");
646 		f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
647 	}
648 
649 	return f;
650 }
651 
652 static int
lws_http_serve(struct lws * wsi,char * uri,const char * origin,const struct lws_http_mount * m)653 lws_http_serve(struct lws *wsi, char *uri, const char *origin,
654 	       const struct lws_http_mount *m)
655 {
656 	const struct lws_protocol_vhost_options *pvo = m->interpret;
657 	struct lws_process_html_args args;
658 	const char *mimetype;
659 #if !defined(_WIN32_WCE)
660 	const struct lws_plat_file_ops *fops;
661 	const char *vpath;
662 	lws_fop_flags_t fflags = LWS_O_RDONLY;
663 #if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
664 	struct _stat32i64 st;
665 #else
666 	struct stat st;
667 #endif
668 	int spin = 0;
669 #endif
670 	char path[256], sym[2048];
671 	unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
672 	unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
673 #if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS)
674 	size_t len;
675 #endif
676 	int n;
677 
678 	wsi->handling_404 = 0;
679 	if (!wsi->a.vhost)
680 		return -1;
681 
682 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
683 	if (wsi->a.vhost->http.error_document_404 &&
684 	    !strcmp(uri, wsi->a.vhost->http.error_document_404))
685 		wsi->handling_404 = 1;
686 #endif
687 
688 	lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
689 
690 #if !defined(_WIN32_WCE)
691 
692 	fflags |= lws_vfs_prepare_flags(wsi);
693 
694 	do {
695 		spin++;
696 		fops = lws_vfs_select_fops(wsi->a.context->fops, path, &vpath);
697 
698 		if (wsi->http.fop_fd)
699 			lws_vfs_file_close(&wsi->http.fop_fd);
700 
701 		wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->a.context->fops,
702 							path, vpath, &fflags);
703 		if (!wsi->http.fop_fd) {
704 			lwsl_info("%s: Unable to open '%s': errno %d\n",
705 				  __func__, path, errno);
706 
707 			return 1;
708 		}
709 
710 		/* if it can't be statted, don't try */
711 		if (fflags & LWS_FOP_FLAG_VIRTUAL)
712 			break;
713 #if defined(LWS_PLAT_FREERTOS)
714 		break;
715 #endif
716 #if !defined(WIN32)
717 		if (fstat(wsi->http.fop_fd->fd, &st)) {
718 			lwsl_info("unable to stat %s\n", path);
719 			goto notfound;
720 		}
721 #else
722 #if defined(LWS_HAVE__STAT32I64)
723 		{
724 			WCHAR buf[MAX_PATH];
725 			MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, LWS_ARRAY_SIZE(buf));
726 			if (_wstat32i64(buf, &st)) {
727 				lwsl_info("unable to stat %s\n", path);
728 				goto notfound;
729 			}
730 		}
731 #else
732 		if (stat(path, &st)) {
733 			lwsl_info("unable to stat %s\n", path);
734 			goto notfound;
735 		}
736 #endif
737 #endif
738 
739 		wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime;
740 		fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
741 
742 #if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS)
743 		if ((S_IFMT & st.st_mode) == S_IFLNK) {
744 			len = (size_t)readlink(path, sym, sizeof(sym) - 1);
745 			if (len) {
746 				lwsl_err("Failed to read link %s\n", path);
747 				goto notfound;
748 			}
749 			sym[len] = '\0';
750 			lwsl_debug("symlink %s -> %s\n", path, sym);
751 			lws_snprintf(path, sizeof(path) - 1, "%s", sym);
752 		}
753 #endif
754 		if ((S_IFMT & st.st_mode) == S_IFDIR) {
755 			lwsl_debug("default filename append to dir\n");
756 			lws_snprintf(path, sizeof(path) - 1, "%s/%s/%s",
757 				 origin, uri, m->def ? m->def : "index.html");
758 		}
759 
760 	} while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
761 
762 	if (spin == 5)
763 		lwsl_err("symlink loop %s \n", path);
764 
765 	n = sprintf(sym, "%08llX%08lX",
766 		    (unsigned long long)lws_vfs_get_length(wsi->http.fop_fd),
767 		    (unsigned long)lws_vfs_get_mod_time(wsi->http.fop_fd));
768 
769 	/* disable ranges if IF_RANGE token invalid */
770 
771 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))
772 		if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))
773 			/* differs - defeat Range: */
774 			wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
775 
776 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
777 		/*
778 		 * he thinks he has some version of it already,
779 		 * check if the tag matches
780 		 */
781 		if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
782 					WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
783 
784 			char cache_control[50], *cc = "no-store";
785 			int cclen = 8;
786 
787 			lwsl_debug("%s: ETAG match %s %s\n", __func__,
788 				   uri, origin);
789 
790 			/* we don't need to send the payload */
791 			if (lws_add_http_header_status(wsi,
792 					HTTP_STATUS_NOT_MODIFIED, &p, end)) {
793 				lwsl_err("%s: failed adding not modified\n",
794 						__func__);
795 				return -1;
796 			}
797 
798 			if (lws_add_http_header_by_token(wsi,
799 					WSI_TOKEN_HTTP_ETAG,
800 					(unsigned char *)sym, n, &p, end))
801 				return -1;
802 
803 			/* but we still need to send cache control... */
804 
805 			if (m->cache_max_age && m->cache_reusable) {
806 				if (!m->cache_revalidate) {
807 					cc = cache_control;
808 					cclen = sprintf(cache_control,
809 						"%s, max-age=%u",
810 						intermediates[wsi->cache_intermediaries],
811 						m->cache_max_age);
812 				} else {
813 					cc = cache_control;
814                                         cclen = sprintf(cache_control,
815                                         	"must-revalidate, %s, max-age=%u",
816                                                 intermediates[wsi->cache_intermediaries],
817                                                 m->cache_max_age);
818 				}
819 			}
820 
821 			if (lws_add_http_header_by_token(wsi,
822 					WSI_TOKEN_HTTP_CACHE_CONTROL,
823 					(unsigned char *)cc, cclen, &p, end))
824 				return -1;
825 
826 			if (lws_finalize_http_header(wsi, &p, end))
827 				return -1;
828 
829 			n = lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
830 				      LWS_WRITE_HTTP_HEADERS |
831 				      LWS_WRITE_H2_STREAM_END);
832 			if (n != lws_ptr_diff(p, start)) {
833 				lwsl_err("_write returned %d from %ld\n", n,
834 					 (long)(p - start));
835 				return -1;
836 			}
837 
838 			lws_vfs_file_close(&wsi->http.fop_fd);
839 
840 			if (lws_http_transaction_completed(wsi))
841 				return -1;
842 
843 			return 0;
844 		}
845 	}
846 
847 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
848 			(unsigned char *)sym, n, &p, end))
849 		return -1;
850 #endif
851 
852 	mimetype = lws_get_mimetype(path, m);
853 	if (!mimetype) {
854 		lwsl_info("unknown mimetype for %s\n", path);
855 		if (lws_return_http_status(wsi,
856 				HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL) ||
857 		    lws_http_transaction_completed(wsi))
858 			return -1;
859 
860 		return 0;
861 	}
862 	if (!mimetype[0])
863 		lwsl_debug("sending no mimetype for %s\n", path);
864 
865 	wsi->sending_chunked = 0;
866 	wsi->interpreting = 0;
867 
868 	/*
869 	 * check if this is in the list of file suffixes to be interpreted by
870 	 * a protocol
871 	 */
872 	while (pvo) {
873 		n = (int)strlen(path);
874 		if (n > (int)strlen(pvo->name) &&
875 		    !strcmp(&path[(unsigned int)n - strlen(pvo->name)], pvo->name)) {
876 			wsi->interpreting = 1;
877 			if (!wsi->mux_substream)
878 				wsi->sending_chunked = 1;
879 
880 			wsi->protocol_interpret_idx = (char)(
881 				lws_vhost_name_to_protocol(wsi->a.vhost,
882 							   pvo->value) -
883 				&lws_get_vhost(wsi)->protocols[0]);
884 
885 			lwsl_debug("want %s interpreted by %s (pcol is %s)\n", path,
886 				    wsi->a.vhost->protocols[
887 				             (int)wsi->protocol_interpret_idx].name,
888 				             wsi->a.protocol->name);
889 			if (lws_bind_protocol(wsi, &wsi->a.vhost->protocols[
890 			          (int)wsi->protocol_interpret_idx], __func__))
891 				return -1;
892 
893 			if (lws_ensure_user_space(wsi))
894 				return -1;
895 			break;
896 		}
897 		pvo = pvo->next;
898 	}
899 
900 	if (wsi->sending_chunked) {
901 		if (lws_add_http_header_by_token(wsi,
902 				WSI_TOKEN_HTTP_TRANSFER_ENCODING,
903 				(unsigned char *)"chunked", 7,
904 				&p, end))
905 			return -1;
906 	}
907 
908 	if (m->protocol) {
909 		const struct lws_protocols *pp = lws_vhost_name_to_protocol(
910 						       wsi->a.vhost, m->protocol);
911 
912 		if (lws_bind_protocol(wsi, pp, __func__))
913 			return -1;
914 		args.p = (char *)p;
915 		args.max_len = lws_ptr_diff(end, p);
916 		if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
917 					  wsi->user_space, &args, 0))
918 			return -1;
919 		p = (unsigned char *)args.p;
920 	}
921 
922 	*p = '\0';
923 	n = lws_serve_http_file(wsi, path, mimetype, (char *)start,
924 				lws_ptr_diff(p, start));
925 
926 	if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
927 		return -1; /* error or can't reuse connection: close the socket */
928 
929 	return 0;
930 
931 notfound:
932 
933 	return 1;
934 }
935 #endif
936 
937 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
938 const struct lws_http_mount *
lws_find_mount(struct lws * wsi,const char * uri_ptr,int uri_len)939 lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
940 {
941 	const struct lws_http_mount *hm, *hit = NULL;
942 	int best = 0;
943 
944 	hm = wsi->a.vhost->http.mount_list;
945 	while (hm) {
946 		if (uri_len >= hm->mountpoint_len &&
947 		    !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
948 		    (uri_ptr[hm->mountpoint_len] == '\0' ||
949 		     uri_ptr[hm->mountpoint_len] == '/' ||
950 		     hm->mountpoint_len == 1)
951 		    ) {
952 #if defined(LWS_WITH_SYS_METRICS)
953 			lws_metrics_tag_wsi_add(wsi, "mnt", hm->mountpoint);
954 #endif
955 
956 			if (hm->origin_protocol == LWSMPRO_CALLBACK ||
957 			    ((hm->origin_protocol == LWSMPRO_CGI ||
958 			     lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
959 			     lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
960 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
961 			     lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI) ||
962 			     lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
963 			     lws_hdr_total_length(wsi, WSI_TOKEN_DELETE_URI) ||
964 #endif
965 			     lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI) ||
966 #if defined(LWS_ROLE_H2)
967 			     (wsi->mux_substream &&
968 				lws_hdr_total_length(wsi,
969 						WSI_TOKEN_HTTP_COLON_PATH)) ||
970 #endif
971 			     hm->protocol) &&
972 			    hm->mountpoint_len > best)) {
973 				best = hm->mountpoint_len;
974 				hit = hm;
975 			}
976 		}
977 		hm = hm->mount_next;
978 	}
979 
980 	return hit;
981 }
982 #endif
983 
984 #if defined(LWS_WITH_HTTP_BASIC_AUTH) && !defined(LWS_PLAT_FREERTOS) && defined(LWS_WITH_FILE_OPS)
985 static int
lws_find_string_in_file(const char * filename,const char * string,int stringlen)986 lws_find_string_in_file(const char *filename, const char *string, int stringlen)
987 {
988 	char buf[128];
989 	int fd, match = 0, pos = 0, n = 0, hit = 0;
990 
991 	fd = lws_open(filename, O_RDONLY);
992 	if (fd < 0) {
993 		lwsl_err("can't open auth file: %s\n", filename);
994 		return 0;
995 	}
996 
997 	while (1) {
998 		if (pos == n) {
999 			n = (int)read(fd, buf, sizeof(buf));
1000 			if (n <= 0) {
1001 				if (match == stringlen)
1002 					hit = 1;
1003 				break;
1004 			}
1005 			pos = 0;
1006 		}
1007 
1008 		if (match == stringlen) {
1009 			if (buf[pos] == '\r' || buf[pos] == '\n') {
1010 				hit = 1;
1011 				break;
1012 			}
1013 			match = 0;
1014 		}
1015 
1016 		if (buf[pos] == string[match])
1017 			match++;
1018 		else
1019 			match = 0;
1020 
1021 		pos++;
1022 	}
1023 
1024 	close(fd);
1025 
1026 	return hit;
1027 }
1028 #endif
1029 
1030 #if defined(LWS_WITH_HTTP_BASIC_AUTH)
1031 
1032 int
lws_unauthorised_basic_auth(struct lws * wsi)1033 lws_unauthorised_basic_auth(struct lws *wsi)
1034 {
1035 	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
1036 	unsigned char *start = pt->serv_buf + LWS_PRE,
1037 		      *p = start, *end = p + 2048;
1038 	char buf[64];
1039 	int n;
1040 
1041 	/* no auth... tell him it is required */
1042 
1043 	if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
1044 		return -1;
1045 
1046 	n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\"");
1047 	if (lws_add_http_header_by_token(wsi,
1048 			WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
1049 			(unsigned char *)buf, n, &p, end))
1050 		return -1;
1051 
1052 	if (lws_add_http_header_content_length(wsi, 0, &p, end))
1053 		return -1;
1054 
1055 	if (lws_finalize_http_header(wsi, &p, end))
1056 		return -1;
1057 
1058 	n = lws_write(wsi, start, lws_ptr_diff_size_t(p, start), LWS_WRITE_HTTP_HEADERS |
1059 					     LWS_WRITE_H2_STREAM_END);
1060 	if (n < 0)
1061 		return -1;
1062 
1063 	return lws_http_transaction_completed(wsi);
1064 
1065 }
1066 
1067 #endif
1068 
lws_clean_url(char * p)1069 int lws_clean_url(char *p)
1070 {
1071 	if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
1072 		p += 4;
1073 		if (*p == 's')
1074 		p++;
1075 		if (*p == ':') {
1076 			p++;
1077 			if (*p == '/')
1078 			p++;
1079 		}
1080 	}
1081 
1082 	while (*p) {
1083 		if (p[0] == '/' && p[1] == '/') {
1084 			char *p1 = p;
1085 			while (*p1) {
1086 				*p1 = p1[1];
1087 				p1++;
1088 			}
1089 			continue;
1090 		}
1091 		p++;
1092 	}
1093 
1094 	return 0;
1095 }
1096 
1097 static const unsigned char methods[] = {
1098 	WSI_TOKEN_GET_URI,
1099 	WSI_TOKEN_POST_URI,
1100 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
1101 	WSI_TOKEN_OPTIONS_URI,
1102 	WSI_TOKEN_PUT_URI,
1103 	WSI_TOKEN_PATCH_URI,
1104 	WSI_TOKEN_DELETE_URI,
1105 #endif
1106 	WSI_TOKEN_CONNECT,
1107 	WSI_TOKEN_HEAD_URI,
1108 #ifdef LWS_WITH_HTTP2
1109 	WSI_TOKEN_HTTP_COLON_PATH,
1110 #endif
1111 };
1112 
1113 int
lws_http_get_uri_and_method(struct lws * wsi,char ** puri_ptr,int * puri_len)1114 lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
1115 {
1116 	int n, count = 0;
1117 
1118 	for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
1119 		if (lws_hdr_total_length(wsi, methods[n]))
1120 			count++;
1121 	if (!count) {
1122 		lwsl_warn("Missing URI in HTTP request\n");
1123 		return -1;
1124 	}
1125 
1126 	if (count != 1 &&
1127 	    !((wsi->mux_substream || wsi->h2_stream_carries_ws)
1128 #if defined(LWS_ROLE_H2)
1129 			    &&
1130 	      lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH)
1131 #endif
1132 	      )) {
1133 		lwsl_warn("multiple methods?\n");
1134 		return -1;
1135 	}
1136 
1137 	for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
1138 		if (lws_hdr_total_length(wsi, methods[n])) {
1139 			*puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
1140 			*puri_len = lws_hdr_total_length(wsi, methods[n]);
1141 			return n;
1142 		}
1143 
1144 	return -1;
1145 }
1146 
1147 #if defined(LWS_WITH_HTTP_BASIC_AUTH)
1148 
1149 enum lws_check_basic_auth_results
lws_check_basic_auth(struct lws * wsi,const char * basic_auth_login_file,unsigned int auth_mode)1150 lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file,
1151 		     unsigned int auth_mode)
1152 {
1153 #if defined(LWS_WITH_FILE_OPS)
1154 	char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;
1155 	int m, ml, fi, bar;
1156 
1157 	if (!basic_auth_login_file && auth_mode == LWSAUTHM_DEFAULT)
1158 		return LCBA_CONTINUE;
1159 
1160 	/* Did he send auth? */
1161 	ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
1162 	if (!ml)
1163 		return LCBA_FAILED_AUTH;
1164 
1165 	/* Disallow fragmentation monkey business */
1166 
1167 	fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_AUTHORIZATION];
1168 	if (wsi->http.ah->frags[fi].nfrag) {
1169 		lwsl_err("fragmented basic auth header not allowed\n");
1170 		return LCBA_FAILED_AUTH;
1171 	}
1172 
1173 	m = lws_hdr_copy(wsi, b64, sizeof(b64),
1174 			 WSI_TOKEN_HTTP_AUTHORIZATION);
1175 	if (m < 7) {
1176 		lwsl_err("b64 auth too long\n");
1177 		return LCBA_END_TRANSACTION;
1178 	}
1179 
1180 	b64[5] = '\0';
1181 	if (strcasecmp(b64, "Basic")) {
1182 		lwsl_err("auth missing basic: %s\n", b64);
1183 		return LCBA_END_TRANSACTION;
1184 	}
1185 
1186 	/* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
1187 
1188 	m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1);
1189 	if (m < 0) {
1190 		lwsl_err("plain auth too long\n");
1191 		return LCBA_END_TRANSACTION;
1192 	}
1193 
1194 	plain[m] = '\0';
1195 	pcolon = strchr(plain, ':');
1196 	if (!pcolon) {
1197 		lwsl_err("basic auth format broken\n");
1198 		return LCBA_END_TRANSACTION;
1199 	}
1200 
1201 	switch (auth_mode) {
1202 	case LWSAUTHM_DEFAULT:
1203 		if (lws_find_string_in_file(basic_auth_login_file, plain, m))
1204 			break;
1205 		lwsl_err("%s: basic auth lookup failed\n", __func__);
1206 		return LCBA_FAILED_AUTH;
1207 
1208 	case LWSAUTHM_BASIC_AUTH_CALLBACK:
1209 		bar = wsi->a.protocol->callback(wsi,
1210 				LWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION,
1211 				wsi->user_space, plain, (unsigned int)m);
1212 		if (!bar)
1213 			return LCBA_FAILED_AUTH;
1214 		break;
1215 	default:
1216 		/* Invalid auth mode so lets fail all authentication attempts */
1217 		return LCBA_FAILED_AUTH;
1218 	}
1219 
1220 	/*
1221 	 * Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the
1222 	 * authorized username
1223 	 */
1224 
1225 	*pcolon = '\0';
1226 	wsi->http.ah->frags[fi].len = (uint16_t)lws_ptr_diff_size_t(pcolon, &plain[0]);
1227 	pcolon = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
1228 	strncpy(pcolon, plain, (unsigned int)(ml - 1));
1229 	pcolon[ml - 1] = '\0';
1230 	lwsl_info("%s: basic auth accepted for %s\n", __func__,
1231 		 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION));
1232 
1233 	return LCBA_CONTINUE;
1234 #else
1235 	return LCBA_FAILED_AUTH;
1236 #endif
1237 }
1238 
1239 #endif
1240 
1241 #if defined(LWS_WITH_HTTP_PROXY)
1242 /*
1243  * Set up an onward http proxy connection according to the mount this
1244  * uri falls under.  Notice this can also be starting the proxying of what was
1245  * originally an incoming h1 upgrade, or an h2 ws "upgrade".
1246  */
1247 int
lws_http_proxy_start(struct lws * wsi,const struct lws_http_mount * hit,char * uri_ptr,char ws)1248 lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
1249 		     char *uri_ptr, char ws)
1250 {
1251 	char ads[96], host[96], *pcolon, *pslash, unix_skt = 0;
1252 	struct lws_client_connect_info i;
1253 	struct lws *cwsi;
1254 	int n, na;
1255 	unsigned int max_http_header_data = wsi->a.context->max_http_header_data > 256 ? wsi->a.context->max_http_header_data : 256;
1256 	char rpath[max_http_header_data];
1257 
1258 #if defined(LWS_ROLE_WS)
1259 	if (ws)
1260 		/*
1261 		 * Neither our inbound ws upgrade request side, nor our onward
1262 		 * ws client connection on our side can bind to the actual
1263 		 * protocol that only the remote inbound side and the remote
1264 		 * onward side understand.
1265 		 *
1266 		 * Instead these are both bound to our built-in "lws-ws-proxy"
1267 		 * protocol, which understands how to proxy between the two
1268 		 * sides.
1269 		 *
1270 		 * We bind the parent, inbound part here and our side of the
1271 		 * onward client connection is bound to the same handler using
1272 		 * the .local_protocol_name.
1273 		 */
1274 		lws_bind_protocol(wsi, &lws_ws_proxy, __func__);
1275 #endif
1276 	memset(&i, 0, sizeof(i));
1277 	i.context = lws_get_context(wsi);
1278 
1279 	if (hit->origin[0] == '+')
1280 		unix_skt = 1;
1281 
1282 	pcolon = strchr(hit->origin, ':');
1283 	pslash = strchr(hit->origin, '/');
1284 	if (!pslash) {
1285 		lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin);
1286 		return -1;
1287 	}
1288 
1289 	if (unix_skt) {
1290 		if (!pcolon) {
1291 			lwsl_err("Proxy mount origin for unix skt must "
1292 				 "have address delimited by :\n");
1293 
1294 			return -1;
1295 		}
1296 		n = lws_ptr_diff(pcolon, hit->origin);
1297 		pslash = pcolon;
1298 	} else {
1299 		if (pcolon > pslash)
1300 			pcolon = NULL;
1301 
1302 		if (pcolon)
1303 			n = (int)(pcolon - hit->origin);
1304 		else
1305 			n = (int)(pslash - hit->origin);
1306 
1307 		if (n >= (int)sizeof(ads) - 2)
1308 			n = sizeof(ads) - 2;
1309 	}
1310 
1311 	memcpy(ads, hit->origin, (unsigned int)n);
1312 	ads[n] = '\0';
1313 
1314 	i.address = ads;
1315 	i.port = 80;
1316 	if (hit->origin_protocol == LWSMPRO_HTTPS) {
1317 		i.port = 443;
1318 		i.ssl_connection = 1;
1319 	}
1320 	if (pcolon)
1321 		i.port = atoi(pcolon + 1);
1322 
1323 	n = lws_snprintf(rpath, max_http_header_data - 1, "/%s/%s",
1324 			 pslash + 1, uri_ptr + hit->mountpoint_len) - 1;
1325 	lws_clean_url(rpath);
1326 	n = (int)strlen(rpath);
1327 	if (n && rpath[n - 1] == '/')
1328 		n--;
1329 
1330 	na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
1331 	if (na) {
1332 		char *p;
1333 		int budg;
1334 
1335 		if (!n) /* don't start with the ?... use the first / if so */
1336 			n++;
1337 
1338 		p = rpath + n;
1339 
1340 		if (na >= (int)max_http_header_data - n - 2) {
1341 			lwsl_info("%s: query string %d longer "
1342 				  "than we can handle\n", __func__,
1343 				  na);
1344 
1345 			return -1;
1346 		}
1347 
1348 		*p++ = '?';
1349 		budg = lws_hdr_copy(wsi, p,
1350 			     (int)(&rpath[max_http_header_data - 1] - p),
1351 			     WSI_TOKEN_HTTP_URI_ARGS);
1352 	       if (budg > 0)
1353 		       p += budg;
1354 
1355 		*p = '\0';
1356 	}
1357 
1358 	i.path = rpath;
1359 	lwsl_notice("%s: proxied path '%s'\n", __func__, i.path);
1360 
1361 	/* incoming may be h1 or h2... if he sends h1 HOST, use that
1362 	 * directly, otherwise we must convert h2 :authority to h1
1363 	 * host */
1364 
1365 	i.host = NULL;
1366 #if defined(LWS_ROLE_H2)
1367 	n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1368 	if (n > 0)
1369 		i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1370 	else
1371 #endif
1372 	{
1373 		n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1374 		if (n > 0) {
1375 			i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1376 		}
1377 	}
1378 
1379 #if 0
1380 	if (i.address[0] != '+' ||
1381 	    !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
1382 		i.host = i.address;
1383 	else
1384 		i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1385 #endif
1386 	i.origin = NULL;
1387 	if (!ws) {
1388 		if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI)
1389 #if defined(LWS_WITH_HTTP2)
1390 								|| (
1391 			lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
1392 			!strcmp(lws_hdr_simple_ptr(wsi,
1393 					WSI_TOKEN_HTTP_COLON_METHOD), "post")
1394 			)
1395 #endif
1396 		)
1397 			i.method = "POST";
1398 		else if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PUT_URI)
1399 #if defined(LWS_WITH_HTTP2)
1400 								|| (
1401 			lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
1402 			!strcmp(lws_hdr_simple_ptr(wsi,
1403 					WSI_TOKEN_HTTP_COLON_METHOD), "put")
1404 			)
1405 #endif
1406 		)
1407 			i.method = "PUT";
1408 		else if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PATCH_URI)
1409 #if defined(LWS_WITH_HTTP2)
1410 								|| (
1411 			lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
1412 			!strcmp(lws_hdr_simple_ptr(wsi,
1413 					WSI_TOKEN_HTTP_COLON_METHOD), "patch")
1414 			)
1415 #endif
1416 		)
1417 			i.method = "PATCH";
1418 		else if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_DELETE_URI)
1419 #if defined(LWS_WITH_HTTP2)
1420 								|| (
1421 			lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
1422 			!strcmp(lws_hdr_simple_ptr(wsi,
1423 					WSI_TOKEN_HTTP_COLON_METHOD), "delete")
1424 			)
1425 #endif
1426 		)
1427 			i.method = "DELETE";
1428 		else
1429 			i.method = "GET";
1430 	}
1431 
1432 	if (i.host)
1433 		lws_snprintf(host, sizeof(host), "%s:%u", i.host,
1434 					wsi->a.vhost->listen_port);
1435 	else
1436 		lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port);
1437 
1438 	i.host = host;
1439 
1440 	i.alpn = "http/1.1";
1441 	i.parent_wsi = wsi;
1442 	i.pwsi = &cwsi;
1443 #if defined(LWS_ROLE_WS)
1444 	i.protocol = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
1445 	if (ws)
1446 		i.local_protocol_name = "lws-ws-proxy";
1447 #endif
1448 
1449 //	i.uri_replace_from = hit->origin;
1450 //	i.uri_replace_to = hit->mountpoint;
1451 
1452 	lwsl_info("proxying to %s port %d url %s, ssl %d, from %s, to %s\n",
1453 		   i.address, i.port, i.path, i.ssl_connection,
1454 		   i.uri_replace_from, i.uri_replace_to);
1455 
1456 	if (!lws_client_connect_via_info(&i)) {
1457 		lwsl_err("proxy connect fail\n");
1458 
1459 		/*
1460 		 * ... we can't do the proxy action, but we can
1461 		 * cleanly return him a 503 and a description
1462 		 */
1463 
1464 		lws_return_http_status(wsi,
1465 			HTTP_STATUS_SERVICE_UNAVAILABLE,
1466 			"<h1>Service Temporarily Unavailable</h1>"
1467 			"The server is temporarily unable to service "
1468 			"your request due to maintenance downtime or "
1469 			"capacity problems. Please try again later.");
1470 
1471 		return 1;
1472 	}
1473 
1474 	lwsl_info("%s: setting proxy clientside on %s (parent %s)\n",
1475 		  __func__, lws_wsi_tag(cwsi), lws_wsi_tag(lws_get_parent(cwsi)));
1476 
1477 	cwsi->http.proxy_clientside = 1;
1478 	if (ws) {
1479 		wsi->proxied_ws_parent = 1;
1480 		cwsi->h1_ws_proxied = 1;
1481 		if (i.protocol) {
1482 			lwsl_debug("%s: (requesting '%s')\n",
1483 					__func__, i.protocol);
1484 		}
1485 	}
1486 
1487 	return 0;
1488 }
1489 #endif
1490 
1491 
1492 static const char * const oprot[] = {
1493 	"http://", "https://"
1494 };
1495 
1496 
1497 static int
lws_http_redirect_hit(struct lws_context_per_thread * pt,struct lws * wsi,const struct lws_http_mount * hit,char * uri_ptr,int uri_len,int * h)1498 lws_http_redirect_hit(struct lws_context_per_thread *pt, struct lws *wsi,
1499 		      const struct lws_http_mount *hit, char *uri_ptr,
1500 		      int uri_len, int *h)
1501 {
1502 	char *s;
1503 	int n;
1504 
1505 	*h = 0;
1506 	s = uri_ptr + hit->mountpoint_len;
1507 
1508 	/*
1509 	 * if we have a mountpoint like https://xxx.com/yyy
1510 	 * there is an implied / at the end for our purposes since
1511 	 * we can only mount on a "directory".
1512 	 *
1513 	 * But if we just go with that, the browser cannot understand
1514 	 * that he is actually looking down one "directory level", so
1515 	 * even though we give him /yyy/abc.html he acts like the
1516 	 * current directory level is /.  So relative urls like "x.png"
1517 	 * wrongly look outside the mountpoint.
1518 	 *
1519 	 * Therefore if we didn't come in on a url with an explicit
1520 	 * / at the end, we must redirect to add it so the browser
1521 	 * understands he is one "directory level" down.
1522 	 */
1523 	if ((hit->mountpoint_len > 1 ||
1524 	     (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1525 	      hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
1526 	    (*s != '/' ||
1527 	     (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1528 	      hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
1529 	    (hit->origin_protocol != LWSMPRO_CGI &&
1530 	     hit->origin_protocol != LWSMPRO_CALLBACK)) {
1531 		unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
1532 			      *end = p + wsi->a.context->pt_serv_buf_size -
1533 					LWS_PRE - 512;
1534 
1535 		*h = 1;
1536 
1537 		lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
1538 
1539 		/* > at start indicates deal with by redirect */
1540 		if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1541 		    hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
1542 			n = lws_snprintf((char *)end, 256, "%s%s",
1543 				    oprot[hit->origin_protocol & 1],
1544 				    hit->origin);
1545 		else {
1546 			if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1547 #if defined(LWS_ROLE_H2)
1548 				if (!lws_hdr_total_length(wsi,
1549 						WSI_TOKEN_HTTP_COLON_AUTHORITY))
1550 #endif
1551 					goto bail_nuke_ah;
1552 #if defined(LWS_ROLE_H2)
1553 				n = lws_snprintf((char *)end, 256,
1554 				    "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
1555 				    lws_hdr_simple_ptr(wsi,
1556 						WSI_TOKEN_HTTP_COLON_AUTHORITY),
1557 				    uri_ptr);
1558 #else
1559 				;
1560 #endif
1561 			} else
1562 				n = lws_snprintf((char *)end, 256,
1563 				    "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
1564 				    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
1565 				    uri_ptr);
1566 		}
1567 
1568 		lws_clean_url((char *)end);
1569 		n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1570 				      end, n, &p, end);
1571 		if ((int)n < 0)
1572 			goto bail_nuke_ah;
1573 
1574 		return lws_http_transaction_completed(wsi);
1575 	}
1576 
1577 	return 0;
1578 
1579 bail_nuke_ah:
1580 	lws_header_table_detach(wsi, 1);
1581 
1582 	return 1;
1583 }
1584 
1585 int
lws_http_action(struct lws * wsi)1586 lws_http_action(struct lws *wsi)
1587 {
1588 	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
1589 	int uri_len = 0, meth, m, http_version_len, ha;
1590 	const struct lws_http_mount *hit = NULL;
1591 	enum http_version request_version;
1592 	struct lws_process_html_args args;
1593 	enum http_conn_type conn_type;
1594 	char content_length_str[32];
1595 	char http_version_str[12];
1596 	char http_conn_str[25];
1597 	char *uri_ptr = NULL;
1598 #if defined(LWS_WITH_FILE_OPS)
1599 	char *s;
1600 #endif
1601 	unsigned int n;
1602 
1603 	meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
1604 	if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names))
1605 		goto bail_nuke_ah;
1606 
1607 	lws_metrics_tag_wsi_add(wsi, "vh", wsi->a.vhost->name);
1608 	lws_metrics_tag_wsi_add(wsi, "meth", method_names[meth]);
1609 
1610 	/* we insist on absolute paths */
1611 
1612 	if (!uri_ptr || uri_ptr[0] != '/') {
1613 		lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1614 
1615 		goto bail_nuke_ah;
1616 	}
1617 
1618 	lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
1619 		  meth, uri_ptr);
1620 
1621 	if (wsi->role_ops &&
1622 	    lws_rops_fidx(wsi->role_ops, LWS_ROPS_check_upgrades))
1623 		switch (lws_rops_func_fidx(wsi->role_ops,
1624 					   LWS_ROPS_check_upgrades).
1625 							check_upgrades(wsi)) {
1626 		case LWS_UPG_RET_DONE:
1627 			return 0;
1628 		case LWS_UPG_RET_CONTINUE:
1629 			break;
1630 		case LWS_UPG_RET_BAIL:
1631 			goto bail_nuke_ah;
1632 		}
1633 
1634 	if (lws_ensure_user_space(wsi))
1635 		goto bail_nuke_ah;
1636 
1637 	/* HTTP header had a content length? */
1638 
1639 	wsi->http.rx_content_length = 0;
1640 	wsi->http.content_length_explicitly_zero = 0;
1641 	if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)
1642 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
1643 			||
1644 	    lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
1645 	    lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI)
1646 #endif
1647 	    )
1648 		wsi->http.rx_content_length = 100 * 1024 * 1024;
1649 
1650 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
1651 	    lws_hdr_copy(wsi, content_length_str,
1652 			 sizeof(content_length_str) - 1,
1653 			 WSI_TOKEN_HTTP_CONTENT_LENGTH) > 0) {
1654 		wsi->http.rx_content_remain = wsi->http.rx_content_length =
1655 				(lws_filepos_t)atoll(content_length_str);
1656 		if (!wsi->http.rx_content_length) {
1657 			wsi->http.content_length_explicitly_zero = 1;
1658 			lwsl_debug("%s: explicit 0 content-length\n", __func__);
1659 		}
1660 	}
1661 
1662 	if (wsi->mux_substream) {
1663 		wsi->http.request_version = HTTP_VERSION_2;
1664 	} else {
1665 		/* http_version? Default to 1.0, override with token: */
1666 		request_version = HTTP_VERSION_1_0;
1667 
1668 		/* Works for single digit HTTP versions. : */
1669 		http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
1670 		if (http_version_len > 7 &&
1671 		    lws_hdr_copy(wsi, http_version_str,
1672 				 sizeof(http_version_str) - 1,
1673 				 WSI_TOKEN_HTTP) > 0 &&
1674 		    http_version_str[5] == '1' && http_version_str[7] == '1')
1675 			request_version = HTTP_VERSION_1_1;
1676 
1677 		wsi->http.request_version = request_version;
1678 
1679 		/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
1680 		if (request_version == HTTP_VERSION_1_1)
1681 			conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1682 		else
1683 			conn_type = HTTP_CONNECTION_CLOSE;
1684 
1685 		/* Override default if http "Connection:" header: */
1686 		if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION) &&
1687 		    lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
1688 				 WSI_TOKEN_CONNECTION) > 0) {
1689 			http_conn_str[sizeof(http_conn_str) - 1] = '\0';
1690 			if (!strcasecmp(http_conn_str, "keep-alive"))
1691 				conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1692 			else
1693 				if (!strcasecmp(http_conn_str, "close"))
1694 					conn_type = HTTP_CONNECTION_CLOSE;
1695 		}
1696 		wsi->http.conn_type = conn_type;
1697 	}
1698 
1699 	n = (unsigned int)wsi->a.protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
1700 				    wsi->user_space, uri_ptr, (unsigned int)uri_len);
1701 	if (n) {
1702 		lwsl_info("LWS_CALLBACK_HTTP closing\n");
1703 
1704 		return 1;
1705 	}
1706 	/*
1707 	 * if there is content supposed to be coming,
1708 	 * put a timeout on it having arrived
1709 	 */
1710 	if (!wsi->mux_stream_immortal)
1711 		lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
1712 				(int)wsi->a.context->timeout_secs);
1713 #if defined(LWS_WITH_TLS)
1714 	if (wsi->tls.redirect_to_https) {
1715 		/*
1716 		 * We accepted http:// only so we could redirect to
1717 		 * https://, so issue the redirect.  Create the redirection
1718 		 * URI from the host: header, and regenerate the path part from
1719 		 * the parsed pieces
1720 		 */
1721 		unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
1722 			      *end = p + wsi->a.context->pt_serv_buf_size -
1723 				     LWS_PRE;
1724 
1725 		n = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1726 		if (!n || n > 128)
1727 			goto bail_nuke_ah;
1728 
1729 		if (!lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
1730 			goto bail_nuke_ah;
1731 
1732 		p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "https://");
1733 		memcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n);
1734 		p += n;
1735 		*p++ = '/';
1736 		if (uri_len >= lws_ptr_diff(end, p))
1737 			goto bail_nuke_ah;
1738 
1739 		if (uri_ptr[0])
1740 			p--;
1741 		memcpy(p, uri_ptr, (unsigned int)uri_len);
1742 		p += uri_len;
1743 
1744 		n = 0;
1745 		while (lws_hdr_copy_fragment(wsi, (char *)p + 1,
1746 					     lws_ptr_diff(end, p) - 2,
1747 					     WSI_TOKEN_HTTP_URI_ARGS, (int)n) > 0) {
1748 			*p = n ? '&' : '?';
1749 			p += strlen((char *)p);
1750 			if (p >= end - 2)
1751 				goto bail_nuke_ah;
1752 			n++;
1753 		}
1754 
1755 		n = (unsigned int)lws_ptr_diff(p, start);
1756 
1757 		p += LWS_PRE;
1758 		n = (unsigned int)lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1759 				      start, (int)n, &p, end);
1760 		if ((int)n < 0)
1761 			goto bail_nuke_ah;
1762 
1763 		return lws_http_transaction_completed(wsi);
1764 	}
1765 #endif
1766 
1767 #ifdef LWS_WITH_ACCESS_LOG
1768 	lws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth);
1769 #endif
1770 
1771 	/* can we serve it from the mount list? */
1772 
1773 	hit = lws_find_mount(wsi, uri_ptr, uri_len);
1774 	if (!hit) {
1775 		/* deferred cleanup and reset to protocols[0] */
1776 
1777 		lwsl_info("no hit\n");
1778 
1779 		if (lws_bind_protocol(wsi, &wsi->a.vhost->protocols[0],
1780 				      "no mount hit"))
1781 			return 1;
1782 
1783 		lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1784 
1785 		m = wsi->a.protocol->callback(wsi, LWS_CALLBACK_HTTP,
1786 				    wsi->user_space, uri_ptr, (unsigned int)uri_len);
1787 
1788 		goto after;
1789 	}
1790 
1791 #if defined(LWS_WITH_FILE_OPS)
1792 	s = uri_ptr + hit->mountpoint_len;
1793 #endif
1794 	n = (unsigned int)lws_http_redirect_hit(pt, wsi, hit, uri_ptr, uri_len, &ha);
1795 	if (ha)
1796 		return (int)n;
1797 
1798 #if defined(LWS_WITH_HTTP_BASIC_AUTH)
1799 
1800 	/* basic auth? */
1801 
1802 	switch (lws_check_basic_auth(wsi, hit->basic_auth_login_file,
1803 				     hit->auth_mask & AUTH_MODE_MASK)) {
1804 	case LCBA_CONTINUE:
1805 		break;
1806 	case LCBA_FAILED_AUTH:
1807 		return lws_unauthorised_basic_auth(wsi);
1808 	case LCBA_END_TRANSACTION:
1809 		lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1810 		return lws_http_transaction_completed(wsi);
1811 	}
1812 #endif
1813 
1814 #if defined(LWS_WITH_HTTP_PROXY)
1815 	/*
1816 	 * The mount is a reverse proxy?
1817 	 */
1818 
1819 	// if (hit)
1820 	// lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
1821 	//else
1822 	//	lwsl_notice("%s: no hit\n", __func__);
1823 
1824 	if (hit->origin_protocol == LWSMPRO_HTTPS ||
1825 	    hit->origin_protocol == LWSMPRO_HTTP) {
1826 		n = (unsigned int)lws_http_proxy_start(wsi, hit, uri_ptr, 0);
1827 		// lwsl_notice("proxy start says %d\n", n);
1828 		if (n)
1829 			return (int)n;
1830 
1831 		goto deal_body;
1832 	}
1833 #endif
1834 
1835 	/*
1836 	 * A particular protocol callback is mounted here?
1837 	 *
1838 	 * For the duration of this http transaction, bind us to the
1839 	 * associated protocol
1840 	 */
1841 	if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
1842 		const struct lws_protocols *pp;
1843 		const char *name = hit->origin;
1844 		if (hit->protocol)
1845 			name = hit->protocol;
1846 
1847 		pp = lws_vhost_name_to_protocol(wsi->a.vhost, name);
1848 		if (!pp) {
1849 			lwsl_err("Unable to find plugin '%s'\n",
1850 				 hit->origin);
1851 			return 1;
1852 		}
1853 
1854 		if (lws_bind_protocol(wsi, pp, "http action CALLBACK bind"))
1855 			return 1;
1856 
1857 		lwsl_debug("%s: %s, checking access rights for mask 0x%x\n",
1858 				__func__, hit->origin, hit->auth_mask);
1859 
1860 		args.p = uri_ptr;
1861 		args.len = uri_len;
1862 		args.max_len = hit->auth_mask & ~AUTH_MODE_MASK;
1863 		args.final = 0; /* used to signal callback dealt with it */
1864 		args.chunked = 0;
1865 
1866 		n = (unsigned int)wsi->a.protocol->callback(wsi,
1867 					    LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
1868 					    wsi->user_space, &args, 0);
1869 		if (n) {
1870 			lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
1871 					       NULL);
1872 			goto bail_nuke_ah;
1873 		}
1874 		if (args.final) /* callback completely handled it well */
1875 			return 0;
1876 
1877 		if (hit->cgienv && wsi->a.protocol->callback(wsi,
1878 				LWS_CALLBACK_HTTP_PMO,
1879 				wsi->user_space, (void *)hit->cgienv, 0))
1880 			return 1;
1881 
1882 		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1883 			m = wsi->a.protocol->callback(wsi, LWS_CALLBACK_HTTP,
1884 					    wsi->user_space,
1885 					    uri_ptr + hit->mountpoint_len,
1886 					    (unsigned int)uri_len - hit->mountpoint_len);
1887 			goto after;
1888 		}
1889 	}
1890 
1891 #ifdef LWS_WITH_CGI
1892 	/* did we hit something with a cgi:// origin? */
1893 	if (hit->origin_protocol == LWSMPRO_CGI) {
1894 		const char *cmd[] = {
1895 			NULL, /* replace with cgi path */
1896 			NULL
1897 		};
1898 
1899 		lwsl_debug("%s: cgi\n", __func__);
1900 		cmd[0] = hit->origin;
1901 
1902 		n = 5;
1903 		if (hit->cgi_timeout)
1904 			n = (unsigned int)hit->cgi_timeout;
1905 
1906 		n = (unsigned int)lws_cgi(wsi, cmd, hit->mountpoint_len, (int)n,
1907 			    hit->cgienv);
1908 		if (n) {
1909 			lwsl_err("%s: cgi failed\n", __func__);
1910 			return -1;
1911 		}
1912 
1913 		goto deal_body;
1914 	}
1915 #endif
1916 
1917 #if defined(LWS_WITH_FILE_OPS)
1918 	n = (unsigned int)(uri_len - lws_ptr_diff(s, uri_ptr));
1919 	if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
1920 		s = (char *)hit->def;
1921 	if (!s)
1922 		s = "index.html";
1923 #endif
1924 
1925 	wsi->cache_secs = (unsigned int)hit->cache_max_age;
1926 	wsi->cache_reuse = hit->cache_reusable;
1927 	wsi->cache_revalidate = hit->cache_revalidate;
1928 	wsi->cache_intermediaries = hit->cache_intermediaries;
1929 
1930 #if defined(LWS_WITH_FILE_OPS)
1931 	m = 1;
1932 	if (hit->origin_protocol == LWSMPRO_FILE)
1933 		m = lws_http_serve(wsi, s, hit->origin, hit);
1934 
1935 	if (m > 0)
1936 #endif
1937 	{
1938 		/*
1939 		 * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
1940 		 */
1941 		if (hit->protocol) {
1942 			const struct lws_protocols *pp =
1943 					lws_vhost_name_to_protocol(
1944 						wsi->a.vhost, hit->protocol);
1945 
1946 			/* coverity */
1947 			if (!pp)
1948 				return 1;
1949 
1950 			lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1951 
1952 			if (lws_bind_protocol(wsi, pp, "http_action HTTP"))
1953 				return 1;
1954 
1955 			m = pp->callback(wsi, LWS_CALLBACK_HTTP,
1956 					 wsi->user_space,
1957 					 uri_ptr + hit->mountpoint_len,
1958 					 (size_t)(uri_len - hit->mountpoint_len));
1959 		} else
1960 			m = wsi->a.protocol->callback(wsi, LWS_CALLBACK_HTTP,
1961 				    wsi->user_space, uri_ptr, (size_t)uri_len);
1962 	}
1963 
1964 after:
1965 	if (m) {
1966 		lwsl_info("LWS_CALLBACK_HTTP closing\n");
1967 
1968 		return 1;
1969 	}
1970 
1971 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
1972 deal_body:
1973 #endif
1974 	/*
1975 	 * If we're not issuing a file, check for content_length or
1976 	 * HTTP keep-alive. No keep-alive header allocation for
1977 	 * ISSUING_FILE, as this uses HTTP/1.0.
1978 	 *
1979 	 * In any case, return 0 and let lws_read decide how to
1980 	 * proceed based on state
1981 	 */
1982 	if (lwsi_state(wsi) == LRS_ISSUING_FILE)
1983 		return 0;
1984 
1985 	/* Prepare to read body if we have a content length: */
1986 	lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
1987 		   (long long)wsi->http.rx_content_length,
1988 		   wsi->upgraded_to_http2, wsi->mux_substream);
1989 
1990 	if (wsi->http.content_length_explicitly_zero &&
1991 	    lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1992 
1993 		/*
1994 		 * POST with an explicit content-length of zero
1995 		 *
1996 		 * If we don't give the user code the empty HTTP_BODY callback,
1997 		 * he may become confused to hear the HTTP_BODY_COMPLETION (due
1998 		 * to, eg, instantiation of lws_spa never happened).
1999 		 *
2000 		 * HTTP_BODY_COMPLETION is responsible for sending the result
2001 		 * status code and result body if any, and to do the transaction
2002 		 * complete processing.
2003 		 */
2004 		if (wsi->a.protocol->callback(wsi, LWS_CALLBACK_HTTP_BODY,
2005 					    wsi->user_space, NULL, 0))
2006 			return 1;
2007 		if (wsi->a.protocol->callback(wsi, LWS_CALLBACK_HTTP_BODY_COMPLETION,
2008 					    wsi->user_space, NULL, 0))
2009 			return 1;
2010 
2011 		return 0;
2012 	}
2013 
2014 	if (wsi->http.rx_content_length <= 0)
2015 		return 0;
2016 
2017 	if (lwsi_state(wsi) != LRS_DISCARD_BODY) {
2018 		lwsi_set_state(wsi, LRS_BODY);
2019 		lwsl_info("%s: %s: LRS_BODY state set (0x%x)\n", __func__,
2020 			  lws_wsi_tag(wsi), (int)wsi->wsistate);
2021 	}
2022 	wsi->http.rx_content_remain = wsi->http.rx_content_length;
2023 
2024 	/*
2025 	 * At this point we have transitioned from deferred
2026 	 * action to expecting BODY on the stream wsi, if it's
2027 	 * in a bundle like h2.  So if the stream wsi has its
2028 	 * own buflist, we need to deal with that first.
2029 	 */
2030 
2031 	while (1) {
2032 		struct lws_tokens ebuf;
2033 		int m;
2034 
2035 		ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist,
2036 							     &ebuf.token);
2037 		if (!ebuf.len)
2038 			break;
2039 
2040 		lwsl_debug("%s: consuming %d\n", __func__, (int)ebuf.len);
2041 		m = lws_read_h1(wsi, ebuf.token, (lws_filepos_t)ebuf.len);
2042 		if (m < 0)
2043 			return -1;
2044 
2045 		if (lws_buflist_aware_finished_consuming(wsi, &ebuf, m, 1,
2046 							 __func__))
2047 			return -1;
2048 	}
2049 
2050 	return 0;
2051 
2052 bail_nuke_ah:
2053 	lws_header_table_detach(wsi, 1);
2054 
2055 	return 1;
2056 }
2057 
2058 int
lws_confirm_host_header(struct lws * wsi)2059 lws_confirm_host_header(struct lws *wsi)
2060 {
2061 	struct lws_tokenize ts;
2062 	lws_tokenize_elem e;
2063 	int port = 80, n;
2064 	char buf[128];
2065 
2066 	/*
2067 	 * this vhost wants us to validate what the
2068 	 * client sent against our vhost name
2069 	 */
2070 
2071 	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
2072 		lwsl_info("%s: missing host on upgrade\n", __func__);
2073 
2074 		return 1;
2075 	}
2076 
2077 #if defined(LWS_WITH_TLS)
2078 	if (wsi->tls.ssl)
2079 		port = 443;
2080 #endif
2081 
2082 	n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);
2083 	if (n <= 0) {
2084 		lwsl_info("%s: missing or oversize host header\n", __func__);
2085 		return 1;
2086 	}
2087 	ts.len = (size_t)n;
2088 	lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|
2089 				    LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|
2090 				    LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);
2091 
2092 	if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
2093 		goto bad_format;
2094 
2095 	if (strncmp(ts.token, wsi->a.vhost->name, ts.token_len)) {
2096 		buf[(size_t)(ts.token - buf) + ts.token_len] = '\0';
2097 		lwsl_info("%s: '%s' in host hdr but vhost name %s\n",
2098 			  __func__, ts.token, wsi->a.vhost->name);
2099 		return 1;
2100 	}
2101 
2102 	e = lws_tokenize(&ts);
2103 	if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
2104 		if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
2105 			goto bad_format;
2106 		else
2107 			port = atoi(ts.token);
2108 	} else
2109 		if (e != LWS_TOKZE_ENDED)
2110 			goto bad_format;
2111 
2112 	if (wsi->a.vhost->listen_port != port) {
2113 		lwsl_info("%s: host port %d mismatches vhost port %d\n",
2114 			  __func__, port, wsi->a.vhost->listen_port);
2115 		return 1;
2116 	}
2117 
2118 	lwsl_debug("%s: host header OK\n", __func__);
2119 
2120 	return 0;
2121 
2122 bad_format:
2123 	lwsl_info("%s: bad host header format\n", __func__);
2124 
2125 	return 1;
2126 }
2127 
2128 #if defined(LWS_WITH_SERVER)
2129 int
lws_http_to_fallback(struct lws * wsi,unsigned char * obuf,size_t olen)2130 lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
2131 {
2132 	const struct lws_role_ops *role = &role_ops_raw_skt;
2133 	const struct lws_protocols *p1, *protocol =
2134 			 &wsi->a.vhost->protocols[wsi->a.vhost->raw_protocol_index];
2135 	char ipbuf[64];
2136 	int n;
2137 
2138 	if (wsi->a.vhost->listen_accept_role &&
2139 	    lws_role_by_name(wsi->a.vhost->listen_accept_role))
2140 		role = lws_role_by_name(wsi->a.vhost->listen_accept_role);
2141 
2142 	if (wsi->a.vhost->listen_accept_protocol) {
2143 		p1 = lws_vhost_name_to_protocol(wsi->a.vhost,
2144 			    wsi->a.vhost->listen_accept_protocol);
2145 		if (p1)
2146 			protocol = p1;
2147 	}
2148 
2149 	lws_bind_protocol(wsi, protocol, __func__);
2150 
2151 	lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role);
2152 
2153 	lws_header_table_detach(wsi, 0);
2154 	lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
2155 
2156 	n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
2157 	if (wsi->role_ops->adoption_cb[1])
2158 		n = wsi->role_ops->adoption_cb[1];
2159 
2160 	ipbuf[0] = '\0';
2161 #if !defined(LWS_PLAT_OPTEE)
2162 	lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));
2163 #endif
2164 
2165 	lwsl_notice("%s: vh %s, peer: %s, role %s, "
2166 		    "protocol %s, cb %d, ah %p\n", __func__, wsi->a.vhost->name,
2167 		    ipbuf, role ? role->name : "null", protocol->name, n,
2168 		    wsi->http.ah);
2169 
2170 	if ((wsi->a.protocol->callback)(wsi, (enum lws_callback_reasons)n, wsi->user_space, NULL, 0))
2171 		return 1;
2172 
2173 	n = LWS_CALLBACK_RAW_RX;
2174 	if (wsi->role_ops->rx_cb[lwsi_role_server(wsi)])
2175 		n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)];
2176 	if (wsi->a.protocol->callback(wsi, (enum lws_callback_reasons)n, wsi->user_space, obuf, olen))
2177 		return 1;
2178 
2179 	return 0;
2180 }
2181 
2182 int
lws_handshake_server(struct lws * wsi,unsigned char ** buf,size_t len)2183 lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
2184 {
2185 	struct lws_context *context = lws_get_context(wsi);
2186 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
2187 #if defined(LWS_WITH_HTTP2)
2188 	struct allocated_headers *ah;
2189 #endif
2190 	unsigned char *obuf = *buf;
2191 #if defined(LWS_WITH_HTTP2)
2192 	char tbuf[128], *p;
2193 #endif
2194 	size_t olen = len;
2195 	int n = 0, m, i;
2196 
2197 	if (len >= 10000000) {
2198 		lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
2199 		assert(0);
2200 	}
2201 
2202 	if (!wsi->http.ah) {
2203 		lwsl_err("%s: assert: NULL ah\n", __func__);
2204 		assert(0);
2205 	}
2206 
2207 	while (len) {
2208 		if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {
2209 			lwsl_err("%s: bad wsi role 0x%x\n", __func__,
2210 					(int)lwsi_role(wsi));
2211 			goto bail_nuke_ah;
2212 		}
2213 
2214 		i = (int)len;
2215 		m = lws_parse(wsi, *buf, &i);
2216 		lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
2217 		(*buf) += (int)len - i;
2218 		len = (unsigned int)i;
2219 
2220 		if (m == LPR_DO_FALLBACK) {
2221 
2222 			/*
2223 			 * http parser went off the rails and
2224 			 * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_
2225 			 * ACCEPT_CONFIG is set on this vhost.
2226 			 *
2227 			 * We are transitioning from http with an AH, to
2228 			 * a backup role (raw-skt, by default).  Drop
2229 			 * the ah, bind to the role with mode as
2230 			 * ESTABLISHED.
2231 			 */
2232 raw_transition:
2233 
2234 			if (lws_http_to_fallback(wsi, obuf, olen)) {
2235 				lwsl_info("%s: fallback -> close\n", __func__);
2236 				goto bail_nuke_ah;
2237 			}
2238 
2239 			(*buf) = obuf + olen;
2240 
2241 			return 0;
2242 		}
2243 		if (m) {
2244 			lwsl_info("lws_parse failed\n");
2245 			goto bail_nuke_ah;
2246 		}
2247 
2248 		/* coverity... */
2249 		if (!wsi->http.ah)
2250 			goto bail_nuke_ah;
2251 
2252 		if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
2253 			continue;
2254 
2255 		lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
2256 
2257 		/* select vhost */
2258 
2259 		if (wsi->a.vhost->listen_port &&
2260 		    lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
2261 			struct lws_vhost *vhost = lws_select_vhost(
2262 				context, wsi->a.vhost->listen_port,
2263 				lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
2264 
2265 			if (vhost)
2266 				lws_vhost_bind_wsi(vhost, wsi);
2267 		} else
2268 			lwsl_info("no host\n");
2269 
2270 		if ((!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) &&
2271 		    (!wsi->conn_stat_done))
2272 			wsi->conn_stat_done = 1;
2273 
2274 		/* check for unwelcome guests */
2275 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
2276 		if (wsi->a.context->reject_service_keywords) {
2277 			const struct lws_protocol_vhost_options *rej =
2278 					wsi->a.context->reject_service_keywords;
2279 			char ua[384], *msg = NULL;
2280 
2281 			if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,
2282 					 WSI_TOKEN_HTTP_USER_AGENT) > 0) {
2283 #ifdef LWS_WITH_ACCESS_LOG
2284 				char *uri_ptr = NULL;
2285 				int meth, uri_len;
2286 #endif
2287 				ua[sizeof(ua) - 1] = '\0';
2288 				while (rej) {
2289 					if (!strstr(ua, rej->name)) {
2290 						rej = rej->next;
2291 						continue;
2292 					}
2293 
2294 					msg = strchr(rej->value, ' ');
2295 					if (msg)
2296 						msg++;
2297 					lws_return_http_status(wsi,
2298 						(unsigned int)atoi(rej->value), msg);
2299 #ifdef LWS_WITH_ACCESS_LOG
2300 					meth = lws_http_get_uri_and_method(wsi,
2301 							&uri_ptr, &uri_len);
2302 					if (meth >= 0)
2303 						lws_prepare_access_log_info(wsi,
2304 							uri_ptr, uri_len, meth);
2305 
2306 					/* wsi close will do the log */
2307 #endif
2308 					/*
2309 					 * We don't want anything from
2310 					 * this rejected guy.  Follow
2311 					 * the close flow, not the
2312 					 * transaction complete flow.
2313 					 */
2314 					goto bail_nuke_ah;
2315 				}
2316 			}
2317 		}
2318 #endif
2319 		/*
2320 		 * So he may have come to us requesting one or another kind
2321 		 * of upgrade from http... but we may want to redirect him at
2322 		 * http level.  In that case, we need to check the redirect
2323 		 * situation even though he's not actually wanting http and
2324 		 * prioritize returning that if there is one.
2325 		 */
2326 
2327 		{
2328 			const struct lws_http_mount *hit = NULL;
2329 			int uri_len = 0, ha, n;
2330 			char *uri_ptr = NULL;
2331 
2332 			n = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
2333 			if (n >= 0) {
2334 				hit = lws_find_mount(wsi, uri_ptr, uri_len);
2335 				if (hit) {
2336 					n = lws_http_redirect_hit(pt, wsi, hit, uri_ptr,
2337 								  uri_len, &ha);
2338 					if (ha)
2339 						return n;
2340 				}
2341 			}
2342 		}
2343 
2344 
2345 
2346 		if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
2347 			lwsl_info("Changing to RAW mode\n");
2348 			goto raw_transition;
2349 		}
2350 
2351 		lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
2352 		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
2353 
2354 		if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
2355 
2356 			const char *up = lws_hdr_simple_ptr(wsi,
2357 							    WSI_TOKEN_UPGRADE);
2358 
2359 			if (strcasecmp(up, "websocket") &&
2360 			    strcasecmp(up, "h2c")) {
2361 				lwsl_info("Unknown upgrade '%s'\n", up);
2362 
2363 				if (lws_return_http_status(wsi,
2364 						HTTP_STATUS_FORBIDDEN, NULL) ||
2365 				    lws_http_transaction_completed(wsi))
2366 					goto bail_nuke_ah;
2367 			}
2368 
2369 			n = user_callback_handle_rxflow(wsi->a.protocol->callback,
2370 					wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,
2371 					wsi->user_space, (char *)up, 0);
2372 
2373 			/* just hang up? */
2374 
2375 			if (n < 0)
2376 				goto bail_nuke_ah;
2377 
2378 			/* callback returned headers already, do t_c? */
2379 
2380 			if (n > 0) {
2381 				if (lws_http_transaction_completed(wsi))
2382 					goto bail_nuke_ah;
2383 
2384 				/* continue on */
2385 
2386 				return 0;
2387 			}
2388 
2389 			/* callback said 0, it was allowed */
2390 
2391 			if (wsi->a.vhost->options &
2392 			    LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
2393 			    lws_confirm_host_header(wsi))
2394 				goto bail_nuke_ah;
2395 
2396 			if (!strcasecmp(up, "websocket")) {
2397 #if defined(LWS_ROLE_WS)
2398 				lws_metrics_tag_wsi_add(wsi, "upg", "ws");
2399 				lwsl_info("Upgrade to ws\n");
2400 				goto upgrade_ws;
2401 #endif
2402 			}
2403 #if defined(LWS_WITH_HTTP2)
2404 			if (!strcasecmp(up, "h2c")) {
2405 				lws_metrics_tag_wsi_add(wsi, "upg", "h2c");
2406 				lwsl_info("Upgrade to h2c\n");
2407 				goto upgrade_h2c;
2408 			}
2409 #endif
2410 		}
2411 
2412 		/* no upgrade ack... he remained as HTTP */
2413 
2414 		lwsl_info("%s: %s: No upgrade\n", __func__, lws_wsi_tag(wsi));
2415 
2416 		lwsi_set_state(wsi, LRS_ESTABLISHED);
2417 #if defined(LWS_WITH_FILE_OPS)
2418 		wsi->http.fop_fd = NULL;
2419 #endif
2420 
2421 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2422 		lws_http_compression_validate(wsi);
2423 #endif
2424 
2425 		lwsl_debug("%s: %s: ah %p\n", __func__, lws_wsi_tag(wsi),
2426 			   (void *)wsi->http.ah);
2427 
2428 		n = lws_http_action(wsi);
2429 
2430 		return n;
2431 
2432 #if defined(LWS_WITH_HTTP2)
2433 upgrade_h2c:
2434 		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
2435 			lwsl_info("missing http2_settings\n");
2436 			goto bail_nuke_ah;
2437 		}
2438 
2439 		lwsl_info("h2c upgrade...\n");
2440 
2441 		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
2442 		/* convert the peer's HTTP-Settings */
2443 		n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
2444 		if (n < 0) {
2445 			lwsl_parser("HTTP2_SETTINGS too long\n");
2446 			return 1;
2447 		}
2448 
2449 		wsi->upgraded_to_http2 = 1;
2450 
2451 		/* adopt the header info */
2452 
2453 		ah = wsi->http.ah;
2454 		lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,
2455 				    &role_ops_h2);
2456 
2457 		/* http2 union member has http union struct at start */
2458 		wsi->http.ah = ah;
2459 
2460 		if (!wsi->h2.h2n) {
2461 			wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n), "h2n");
2462 			if (!wsi->h2.h2n)
2463 				return 1;
2464 		}
2465 
2466 		lws_h2_init(wsi);
2467 
2468 		/* HTTP2 union */
2469 
2470 		lws_h2_settings(wsi, &wsi->h2.h2n->peer_set, (uint8_t *)tbuf, n);
2471 
2472 		if (lws_hpack_dynamic_size(wsi, (int)wsi->h2.h2n->peer_set.s[
2473 		                                      H2SET_HEADER_TABLE_SIZE]))
2474 			return 1;
2475 
2476 		strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
2477 			      "Connection: Upgrade\x0d\x0a"
2478 			      "Upgrade: h2c\x0d\x0a\x0d\x0a");
2479 		m = (int)strlen(tbuf);
2480 		n = lws_issue_raw(wsi, (unsigned char *)tbuf, (unsigned int)m);
2481 		if (n != m) {
2482 			lwsl_debug("http2 switch: ERROR writing to socket\n");
2483 			return 1;
2484 		}
2485 
2486 		return 0;
2487 #endif
2488 #if defined(LWS_ROLE_WS)
2489 upgrade_ws:
2490 		if (lws_process_ws_upgrade(wsi))
2491 			goto bail_nuke_ah;
2492 
2493 		return 0;
2494 #endif
2495 	} /* while all chars are handled */
2496 
2497 	return 0;
2498 
2499 bail_nuke_ah:
2500 	/* drop the header info */
2501 	lws_header_table_detach(wsi, 1);
2502 
2503 	return 1;
2504 }
2505 #endif
2506 
2507 int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed(struct lws * wsi)2508 lws_http_transaction_completed(struct lws *wsi)
2509 {
2510 	int n;
2511 
2512 	if (wsi->http.cgi_transaction_complete)
2513 		return 0;
2514 
2515 	if (lws_has_buffered_out(wsi)
2516 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2517 			|| wsi->http.comp_ctx.buflist_comp ||
2518 	    wsi->http.comp_ctx.may_have_more
2519 #endif
2520 	) {
2521 		/*
2522 		 * ...so he tried to send something large as the http reply,
2523 		 * it went as a partial, but he immediately said the
2524 		 * transaction was completed.
2525 		 *
2526 		 * Defer the transaction completed until the last part of the
2527 		 * partial is sent.
2528 		 */
2529 		lwsl_debug("%s: %s: deferring due to partial\n", __func__,
2530 				lws_wsi_tag(wsi));
2531 		wsi->http.deferred_transaction_completed = 1;
2532 		lws_callback_on_writable(wsi);
2533 
2534 		return 0;
2535 	}
2536 	/*
2537 	 * Are we finishing the transaction before we have consumed any body?
2538 	 *
2539 	 * For h1 this would kill keepalive pipelining, and for h2, considering
2540 	 * it can extend over multiple DATA frames, it would kill the network
2541 	 * connection.
2542 	 */
2543 	if (wsi->http.rx_content_length && wsi->http.rx_content_remain) {
2544 		/*
2545 		 * are we already in LRS_DISCARD_BODY and didn't clear the
2546 		 * remaining before trying to complete the transaction again?
2547 		 */
2548 		if (lwsi_state(wsi) == LRS_DISCARD_BODY)
2549 			return -1;
2550 		/*
2551 		 * let's defer transaction completed processing until we
2552 		 * discarded the remaining body
2553 		 */
2554 		lwsi_set_state(wsi, LRS_DISCARD_BODY);
2555 
2556 		return 0;
2557 	}
2558 
2559 #if defined(LWS_WITH_SYS_METRICS)
2560 	{
2561 		char tmp[10];
2562 
2563 		lws_snprintf(tmp, sizeof(tmp), "%u", wsi->http.response_code);
2564 		lws_metrics_tag_wsi_add(wsi, "status", tmp);
2565 	}
2566 #endif
2567 
2568 	lwsl_info("%s: %s\n", __func__, lws_wsi_tag(wsi));
2569 
2570 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2571 	lws_http_compression_destroy(wsi);
2572 #endif
2573 	lws_access_log(wsi);
2574 
2575 	if (!wsi->hdr_parsing_completed
2576 #if defined(LWS_WITH_CGI)
2577 			&& !wsi->http.cgi
2578 #endif
2579 	) {
2580 		char peer[64];
2581 
2582 #if !defined(LWS_PLAT_OPTEE)
2583 		lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
2584 #else
2585 		peer[0] = '\0';
2586 #endif
2587 		peer[sizeof(peer) - 1] = '\0';
2588 		lwsl_info("%s: (from %s) ignoring, ah parsing incomplete\n",
2589 				__func__, peer);
2590 		return 0;
2591 	}
2592 
2593 #if defined(LWS_WITH_CGI)
2594 	if (wsi->http.cgi) {
2595 		lwsl_debug("%s: cleaning cgi\n", __func__);
2596 		wsi->http.cgi_transaction_complete = 1;
2597 		lws_cgi_remove_and_kill(wsi);
2598 		lws_spawn_piped_destroy(&wsi->http.cgi->lsp);
2599 		lws_sul_cancel(&wsi->http.cgi->sul_grace);
2600 
2601 		lws_free_set_NULL(wsi->http.cgi);
2602 		wsi->http.cgi_transaction_complete = 0;
2603 	}
2604 #endif
2605 
2606 	/* if we can't go back to accept new headers, drop the connection */
2607 	if (wsi->mux_substream)
2608 		return 1;
2609 
2610 	if (wsi->seen_zero_length_recv)
2611 		return 1;
2612 
2613 	if (wsi->http.conn_type != HTTP_CONNECTION_KEEP_ALIVE) {
2614 		lwsl_info("%s: %s: close connection\n", __func__, lws_wsi_tag(wsi));
2615 		return 1;
2616 	}
2617 
2618 	if (lws_bind_protocol(wsi, &wsi->a.vhost->protocols[0], __func__))
2619 		return 1;
2620 
2621 	/*
2622 	 * otherwise set ourselves up ready to go again, but because we have no
2623 	 * idea about the wsi writability, we make put it in a holding state
2624 	 * until we can verify POLLOUT.  The part of this that confirms POLLOUT
2625 	 * with no partials is in lws_server_socket_service() below.
2626 	 */
2627 	lwsl_debug("%s: %s: setting DEF_ACT from 0x%x: %p\n", __func__,
2628 		   lws_wsi_tag(wsi), (int)wsi->wsistate, wsi->buflist);
2629 	lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
2630 	wsi->http.tx_content_length = 0;
2631 	wsi->http.tx_content_remain = 0;
2632 	wsi->hdr_parsing_completed = 0;
2633 	wsi->sending_chunked = 0;
2634 #ifdef LWS_WITH_ACCESS_LOG
2635 	wsi->http.access_log.sent = 0;
2636 #endif
2637 #if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
2638 	if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
2639 	    wsi->http.fop_fd != NULL)
2640 		lws_vfs_file_close(&wsi->http.fop_fd);
2641 #endif
2642 
2643 	n = NO_PENDING_TIMEOUT;
2644 	if (wsi->a.vhost->keepalive_timeout)
2645 		n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
2646 	lws_set_timeout(wsi, (enum pending_timeout)n, wsi->a.vhost->keepalive_timeout);
2647 
2648 	/*
2649 	 * We already know we are on http1.1 / keepalive and the next thing
2650 	 * coming will be another header set.
2651 	 *
2652 	 * If there is no pending rx and we still have the ah, drop it and
2653 	 * reacquire a new ah when the new headers start to arrive.  (Otherwise
2654 	 * we needlessly hog an ah indefinitely.)
2655 	 *
2656 	 * However if there is pending rx and we know from the keepalive state
2657 	 * that is already at least the start of another header set, simply
2658 	 * reset the existing header table and keep it.
2659 	 */
2660 	if (wsi->http.ah) {
2661 		// lws_buflist_describe(&wsi->buflist, wsi, __func__);
2662 		if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
2663 			lwsl_debug("%s: %s: nothing in buflist, detaching ah\n",
2664 				  __func__, lws_wsi_tag(wsi));
2665 			lws_header_table_detach(wsi, 1);
2666 #ifdef LWS_WITH_TLS
2667 			/*
2668 			 * additionally... if we are hogging an SSL instance
2669 			 * with no pending pipelined headers (or ah now), and
2670 			 * SSL is scarce, drop this connection without waiting
2671 			 */
2672 
2673 			if (wsi->a.vhost->tls.use_ssl &&
2674 			    wsi->a.context->simultaneous_ssl_restriction &&
2675 			    wsi->a.context->simultaneous_ssl ==
2676 				   wsi->a.context->simultaneous_ssl_restriction) {
2677 				lwsl_info("%s: simultaneous_ssl_restriction\n",
2678 					  __func__);
2679 				return 1;
2680 			}
2681 #endif
2682 		} else {
2683 			lwsl_info("%s: %s: resetting/keeping ah as pipeline\n",
2684 				  __func__, lws_wsi_tag(wsi));
2685 			lws_header_table_reset(wsi, 0);
2686 			/*
2687 			 * If we kept the ah, we should restrict the amount
2688 			 * of time we are willing to keep it.  Otherwise it
2689 			 * will be bound the whole time the connection remains
2690 			 * open.
2691 			 */
2692 			lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
2693 					wsi->a.vhost->keepalive_timeout);
2694 		}
2695 		/* If we're (re)starting on headers, need other implied init */
2696 		if (wsi->http.ah)
2697 			wsi->http.ah->ues = URIES_IDLE;
2698 
2699 		//lwsi_set_state(wsi, LRS_ESTABLISHED); // !!!
2700 	} else
2701 		if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
2702 			if (lws_header_table_attach(wsi, 0))
2703 				lwsl_debug("acquired ah\n");
2704 
2705 	lwsl_debug("%s: %s: keep-alive await new transaction (state 0x%x)\n",
2706 		   __func__, lws_wsi_tag(wsi), (int)wsi->wsistate);
2707 	lws_callback_on_writable(wsi);
2708 
2709 	return 0;
2710 }
2711 
2712 #if defined(LWS_WITH_FILE_OPS)
2713 int
lws_serve_http_file(struct lws * wsi,const char * file,const char * content_type,const char * other_headers,int other_headers_len)2714 lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
2715 		    const char *other_headers, int other_headers_len)
2716 {
2717 	struct lws_context *context = lws_get_context(wsi);
2718 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
2719 	unsigned char *response = pt->serv_buf + LWS_PRE;
2720 #if defined(LWS_WITH_RANGES)
2721 	struct lws_range_parsing *rp = &wsi->http.range;
2722 #endif
2723 	int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
2724 	char cache_control[50], *cc = "no-store";
2725 	lws_fop_flags_t fflags = LWS_O_RDONLY;
2726 	const struct lws_plat_file_ops *fops;
2727 	lws_filepos_t total_content_length;
2728 	unsigned char *p = response;
2729 	unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
2730 	const char *vpath;
2731 #if defined(LWS_WITH_RANGES)
2732 	int ranges;
2733 #endif
2734 
2735 	if (wsi->handling_404)
2736 		n = HTTP_STATUS_NOT_FOUND;
2737 
2738 	/*
2739 	 * We either call the platform fops .open with first arg platform fops,
2740 	 * or we call fops_zip .open with first arg platform fops, and fops_zip
2741 	 * open will decide whether to switch to fops_zip or stay with fops_def.
2742 	 *
2743 	 * If wsi->http.fop_fd is already set, the caller already opened it
2744 	 */
2745 	if (!wsi->http.fop_fd) {
2746 		fops = lws_vfs_select_fops(wsi->a.context->fops, file, &vpath);
2747 		fflags |= lws_vfs_prepare_flags(wsi);
2748 		wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->a.context->fops,
2749 							file, vpath, &fflags);
2750 		if (!wsi->http.fop_fd) {
2751 			lwsl_info("%s: Unable to open: '%s': errno %d\n",
2752 				  __func__, file, errno);
2753 			if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND,
2754 						   NULL))
2755 						return -1;
2756 			return !wsi->mux_substream;
2757 		}
2758 	}
2759 
2760 	/*
2761 	 * Caution... wsi->http.fop_fd is live from here
2762 	 */
2763 
2764 	wsi->http.filelen = lws_vfs_get_length(wsi->http.fop_fd);
2765 	total_content_length = wsi->http.filelen;
2766 
2767 #if defined(LWS_WITH_RANGES)
2768 	ranges = lws_ranges_init(wsi, rp, wsi->http.filelen);
2769 
2770 	lwsl_debug("Range count %d\n", ranges);
2771 	/*
2772 	 * no ranges -> 200;
2773 	 *  1 range  -> 206 + Content-Type: normal; Content-Range;
2774 	 *  more     -> 206 + Content-Type: multipart/byteranges
2775 	 *  		Repeat the true Content-Type in each multipart header
2776 	 *  		along with Content-Range
2777 	 */
2778 	if (ranges < 0) {
2779 		/* it means he expressed a range in Range:, but it was illegal */
2780 		lws_return_http_status(wsi,
2781 				HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, NULL);
2782 		if (lws_http_transaction_completed(wsi))
2783 			goto bail; /* <0 means just hang up */
2784 
2785 		lws_vfs_file_close(&wsi->http.fop_fd);
2786 
2787 		return 0; /* == 0 means we did the transaction complete */
2788 	}
2789 	if (ranges)
2790 		n = HTTP_STATUS_PARTIAL_CONTENT;
2791 #endif
2792 
2793 	if (lws_add_http_header_status(wsi, (unsigned int)n, &p, end))
2794 		goto bail;
2795 
2796 	if ((wsi->http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
2797 		       LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
2798 	    (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
2799 		if (lws_add_http_header_by_token(wsi,
2800 			WSI_TOKEN_HTTP_CONTENT_ENCODING,
2801 			(unsigned char *)"gzip", 4, &p, end))
2802 			goto bail;
2803 		lwsl_info("file is being provided in gzip\n");
2804 	}
2805 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2806 	else {
2807 		/*
2808 		 * if we know its very compressible, and we can use
2809 		 * compression, then use the most preferred compression
2810 		 * method that the client said he will accept
2811 		 */
2812 
2813 		if (!wsi->interpreting && (
2814 		     !strncmp(content_type, "text/", 5) ||
2815 		     !strcmp(content_type, "application/javascript") ||
2816 		     !strcmp(content_type, "image/svg+xml")))
2817 			lws_http_compression_apply(wsi, NULL, &p, end, 0);
2818 	}
2819 #endif
2820 
2821 	if (
2822 #if defined(LWS_WITH_RANGES)
2823 	    ranges < 2 &&
2824 #endif
2825 	    content_type && content_type[0])
2826 		if (lws_add_http_header_by_token(wsi,
2827 						 WSI_TOKEN_HTTP_CONTENT_TYPE,
2828 						 (unsigned char *)content_type,
2829 						 (int)strlen(content_type),
2830 						 &p, end))
2831 			goto bail;
2832 
2833 #if defined(LWS_WITH_RANGES)
2834 	if (ranges >= 2) { /* multipart byteranges */
2835 		lws_strncpy(wsi->http.multipart_content_type, content_type,
2836 			sizeof(wsi->http.multipart_content_type));
2837 
2838 		if (lws_add_http_header_by_token(wsi,
2839 						 WSI_TOKEN_HTTP_CONTENT_TYPE,
2840 						 (unsigned char *)
2841 						 "multipart/byteranges; "
2842 						 "boundary=_lws",
2843 			 	 	 	 20, &p, end))
2844 			goto bail;
2845 
2846 		/*
2847 		 *  our overall content length has to include
2848 		 *
2849 		 *  - (n + 1) x "_lws\r\n"
2850 		 *  - n x Content-Type: xxx/xxx\r\n
2851 		 *  - n x Content-Range: bytes xxx-yyy/zzz\r\n
2852 		 *  - n x /r/n
2853 		 *  - the actual payloads (aggregated in rp->agg)
2854 		 *
2855 		 *  Precompute it for the main response header
2856 		 */
2857 
2858 		total_content_length = (lws_filepos_t)rp->agg +
2859 				       6 /* final _lws\r\n */;
2860 
2861 		lws_ranges_reset(rp);
2862 		while (lws_ranges_next(rp)) {
2863 			n = lws_snprintf(cache_control, sizeof(cache_control),
2864 					"bytes %llu-%llu/%llu",
2865 					rp->start, rp->end, rp->extent);
2866 
2867 			total_content_length = total_content_length +
2868 					(lws_filepos_t)(
2869 				6 /* header _lws\r\n */ +
2870 				/* Content-Type: xxx/xxx\r\n */
2871 				14 + (int)strlen(content_type) + 2 +
2872 				/* Content-Range: xxxx\r\n */
2873 				15 + n + 2 +
2874 				2); /* /r/n */
2875 		}
2876 
2877 		lws_ranges_reset(rp);
2878 		lws_ranges_next(rp);
2879 	}
2880 
2881 	if (ranges == 1) {
2882 		total_content_length = (lws_filepos_t)rp->agg;
2883 		n = lws_snprintf(cache_control, sizeof(cache_control),
2884 				 "bytes %llu-%llu/%llu",
2885 				 rp->start, rp->end, rp->extent);
2886 
2887 		if (lws_add_http_header_by_token(wsi,
2888 						 WSI_TOKEN_HTTP_CONTENT_RANGE,
2889 						 (unsigned char *)cache_control,
2890 						 n, &p, end))
2891 			goto bail;
2892 	}
2893 
2894 	wsi->http.range.inside = 0;
2895 
2896 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
2897 					 (unsigned char *)"bytes", 5, &p, end))
2898 		goto bail;
2899 #endif
2900 
2901 	if (!wsi->mux_substream) {
2902 		/* for http/1.1 ... */
2903 		if (!wsi->sending_chunked
2904 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2905 				&& !wsi->http.lcs
2906 #endif
2907 		) {
2908 			/* ... if not already using chunked and not using an
2909 			 * http compression translation, then send the naive
2910 			 * content length
2911 			 */
2912 			if (lws_add_http_header_content_length(wsi,
2913 						total_content_length, &p, end))
2914 				goto bail;
2915 		} else {
2916 
2917 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2918 			if (wsi->http.lcs) {
2919 
2920 				/* ...otherwise, for http 1 it must go chunked.
2921 				 * For the compression case, the reason is we
2922 				 * compress on the fly and do not know the
2923 				 * compressed content-length until it has all
2924 				 * been sent.  Http/1.1 pipelining must be able
2925 				 * to know where the transaction boundaries are
2926 				 * ... so chunking...
2927 				 */
2928 				if (lws_add_http_header_by_token(wsi,
2929 						WSI_TOKEN_HTTP_TRANSFER_ENCODING,
2930 						(unsigned char *)"chunked", 7,
2931 						&p, end))
2932 					goto bail;
2933 
2934 				/*
2935 				 * ...this is fun, isn't it :-)  For h1 that is
2936 				 * using an http compression translation, the
2937 				 * compressor must chunk its output privately.
2938 				 *
2939 				 * h2 doesn't need (or support) any of this
2940 				 * crap.
2941 				 */
2942 				lwsl_debug("setting chunking\n");
2943 				wsi->http.comp_ctx.chunking = 1;
2944 			}
2945 #endif
2946 		}
2947 	}
2948 
2949 	if (wsi->cache_secs && wsi->cache_reuse) {
2950 		if (!wsi->cache_revalidate) {
2951 			cc = cache_control;
2952 			cclen = sprintf(cache_control, "%s, max-age=%u",
2953 				    intermediates[wsi->cache_intermediaries],
2954 				    wsi->cache_secs);
2955 		} else {
2956 			cc = cache_control;
2957 			cclen = sprintf(cache_control,
2958 					"must-revalidate, %s, max-age=%u",
2959                                 intermediates[wsi->cache_intermediaries],
2960                                                     wsi->cache_secs);
2961 
2962 		}
2963 	}
2964 
2965 	/* Only add cache control if its not specified by any other_headers. */
2966 	if (!other_headers ||
2967 	    (!strstr(other_headers, "cache-control") &&
2968 	     !strstr(other_headers, "Cache-Control"))) {
2969 		if (lws_add_http_header_by_token(wsi,
2970 				WSI_TOKEN_HTTP_CACHE_CONTROL,
2971 				(unsigned char *)cc, cclen, &p, end))
2972 			goto bail;
2973 	}
2974 
2975 	if (other_headers) {
2976 		if ((end - p) < other_headers_len)
2977 			goto bail;
2978 		memcpy(p, other_headers, (unsigned int)other_headers_len);
2979 		p += other_headers_len;
2980 	}
2981 
2982 	if (lws_finalize_http_header(wsi, &p, end))
2983 		goto bail;
2984 
2985 	ret = lws_write(wsi, response, lws_ptr_diff_size_t(p, response), LWS_WRITE_HTTP_HEADERS);
2986 	if (ret != (p - response)) {
2987 		lwsl_err("_write returned %d from %ld\n", ret,
2988 			 (long)(p - response));
2989 		goto bail;
2990 	}
2991 
2992 	wsi->http.filepos = 0;
2993 	lwsi_set_state(wsi, LRS_ISSUING_FILE);
2994 
2995 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI)) {
2996 		/* we do not emit the body */
2997 		lws_vfs_file_close(&wsi->http.fop_fd);
2998 		if (lws_http_transaction_completed(wsi))
2999 			goto bail;
3000 
3001 		return 0;
3002 	}
3003 
3004 	lws_callback_on_writable(wsi);
3005 
3006 	return 0;
3007 
3008 bail:
3009 	lws_vfs_file_close(&wsi->http.fop_fd);
3010 
3011 	return -1;
3012 }
3013 #endif
3014 
3015 #if defined(LWS_WITH_FILE_OPS)
3016 
lws_serve_http_file_fragment(struct lws * wsi)3017 int lws_serve_http_file_fragment(struct lws *wsi)
3018 {
3019 	struct lws_context *context = wsi->a.context;
3020 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
3021 	struct lws_process_html_args args;
3022 	lws_filepos_t amount, poss;
3023 	unsigned char *p, *pstart;
3024 #if defined(LWS_WITH_RANGES)
3025 	unsigned char finished = 0;
3026 #endif
3027 #if defined(LWS_ROLE_H2)
3028 	struct lws *nwsi;
3029 #endif
3030 	int n, m;
3031 
3032 	lwsl_debug("wsi->mux_substream %d\n", wsi->mux_substream);
3033 
3034 	do {
3035 
3036 		/* priority 1: buffered output */
3037 
3038 		if (lws_has_buffered_out(wsi)) {
3039 			if (lws_issue_raw(wsi, NULL, 0) < 0) {
3040 				lwsl_info("%s: closing\n", __func__);
3041 				goto file_had_it;
3042 			}
3043 			break;
3044 		}
3045 
3046 		/* priority 2: buffered pre-compression-transform */
3047 
3048 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
3049 	if (wsi->http.comp_ctx.buflist_comp ||
3050 	    wsi->http.comp_ctx.may_have_more) {
3051 		enum lws_write_protocol wp = LWS_WRITE_HTTP;
3052 
3053 		lwsl_info("%s: completing comp partial (buflist %p, may %d)\n",
3054 			   __func__, wsi->http.comp_ctx.buflist_comp,
3055 			   wsi->http.comp_ctx.may_have_more);
3056 
3057 		if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_write_role_protocol) &&
3058 		    lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_write_role_protocol).
3059 					write_role_protocol(wsi, NULL, 0, &wp) < 0) {
3060 			lwsl_info("%s signalling to close\n", __func__);
3061 			goto file_had_it;
3062 		}
3063 		lws_callback_on_writable(wsi);
3064 
3065 		break;
3066 	}
3067 #endif
3068 
3069 		if (wsi->http.filepos == wsi->http.filelen)
3070 			goto all_sent;
3071 
3072 		n = 0;
3073 		p = pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
3074 
3075 #if defined(LWS_WITH_RANGES)
3076 		if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
3077 
3078 			lwsl_notice("%s: doing range start %llu\n", __func__,
3079 				    wsi->http.range.start);
3080 
3081 			if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
3082 						   (lws_fileofs_t)wsi->http.range.start -
3083 						   (lws_fileofs_t)wsi->http.filepos) < 0)
3084 				goto file_had_it;
3085 
3086 			wsi->http.filepos = wsi->http.range.start;
3087 
3088 			if (wsi->http.range.count_ranges > 1) {
3089 				n =  lws_snprintf((char *)p,
3090 						context->pt_serv_buf_size -
3091 						LWS_H2_FRAME_HEADER_LENGTH,
3092 					"_lws\x0d\x0a"
3093 					"Content-Type: %s\x0d\x0a"
3094 					"Content-Range: bytes "
3095 						"%llu-%llu/%llu\x0d\x0a"
3096 					"\x0d\x0a",
3097 					wsi->http.multipart_content_type,
3098 					wsi->http.range.start,
3099 					wsi->http.range.end,
3100 					wsi->http.range.extent);
3101 				p += n;
3102 			}
3103 
3104 			wsi->http.range.budget = wsi->http.range.end -
3105 						   wsi->http.range.start + 1;
3106 			wsi->http.range.inside = 1;
3107 		}
3108 #endif
3109 
3110 		poss = context->pt_serv_buf_size;
3111 
3112 #if defined(LWS_ROLE_H2)
3113 		/*
3114 		 * If it's h2, restrict any lump that we are sending to the
3115 		 * max h2 frame size the peer indicated he could handle in
3116 		 * his SETTINGS
3117 		 */
3118 		nwsi = lws_get_network_wsi(wsi);
3119 		if (nwsi->h2.h2n &&
3120 		    poss > (lws_filepos_t)nwsi->h2.h2n->peer_set.s[H2SET_MAX_FRAME_SIZE])
3121 			poss = (lws_filepos_t)nwsi->h2.h2n->peer_set.s[H2SET_MAX_FRAME_SIZE];
3122 #endif
3123 		poss = poss - (lws_filepos_t)(n + LWS_H2_FRAME_HEADER_LENGTH);
3124 
3125 		if (wsi->http.tx_content_length)
3126 			if (poss > wsi->http.tx_content_remain)
3127 				poss = wsi->http.tx_content_remain;
3128 
3129 		/*
3130 		 * If there is a hint about how much we will do well to send at
3131 		 * one time, restrict ourselves to only trying to send that.
3132 		 */
3133 		if (wsi->a.protocol->tx_packet_size &&
3134 		    poss > wsi->a.protocol->tx_packet_size)
3135 			poss = wsi->a.protocol->tx_packet_size;
3136 
3137 		if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_tx_credit)) {
3138 			lws_filepos_t txc = (unsigned int)lws_rops_func_fidx(wsi->role_ops,
3139 							       LWS_ROPS_tx_credit).
3140 					tx_credit(wsi, LWSTXCR_US_TO_PEER, 0);
3141 
3142 			if (!txc) {
3143 				/*
3144 				 * We shouldn't've been able to get the
3145 				 * WRITEABLE if we are skint
3146 				 */
3147 				lwsl_notice("%s: %s: no tx credit\n", __func__,
3148 						lws_wsi_tag(wsi));
3149 
3150 				return 0;
3151 			}
3152 			if (txc < poss)
3153 				poss = txc;
3154 
3155 			/*
3156 			 * Tracking consumption of the actual payload amount
3157 			 * will be handled when the role data frame is sent...
3158 			 */
3159 		}
3160 
3161 #if defined(LWS_WITH_RANGES)
3162 		if (wsi->http.range.count_ranges) {
3163 			if (wsi->http.range.count_ranges > 1)
3164 				poss -= 7; /* allow for final boundary */
3165 			if (poss > wsi->http.range.budget)
3166 				poss = wsi->http.range.budget;
3167 		}
3168 #endif
3169 		if (wsi->sending_chunked) {
3170 			/* we need to drop the chunk size in here */
3171 			p += 10;
3172 			/* allow for the chunk to grow by 128 in translation */
3173 			poss -= 10 + 128;
3174 		}
3175 
3176 		amount = 0;
3177 		if (lws_vfs_file_read(wsi->http.fop_fd, &amount, p, poss) < 0)
3178 			goto file_had_it; /* caller will close */
3179 
3180 		if (wsi->sending_chunked)
3181 			n = (int)amount;
3182 		else
3183 			n = lws_ptr_diff(p, pstart) + (int)amount;
3184 
3185 		lwsl_debug("%s: sending %d\n", __func__, n);
3186 
3187 		if (n) {
3188 			lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
3189 					(int)context->timeout_secs);
3190 
3191 			if (wsi->interpreting) {
3192 				args.p = (char *)p;
3193 				args.len = n;
3194 				args.max_len = (int)(unsigned int)poss + 128;
3195 				args.final = wsi->http.filepos + (unsigned int)n ==
3196 							wsi->http.filelen;
3197 				args.chunked = wsi->sending_chunked;
3198 				if (user_callback_handle_rxflow(
3199 				     wsi->a.vhost->protocols[
3200 				     (int)wsi->protocol_interpret_idx].callback,
3201 				     wsi, LWS_CALLBACK_PROCESS_HTML,
3202 				     wsi->user_space, &args, 0) < 0)
3203 					goto file_had_it;
3204 				n = args.len;
3205 				p = (unsigned char *)args.p;
3206 			} else
3207 				p = pstart;
3208 
3209 #if defined(LWS_WITH_RANGES)
3210 			if (wsi->http.range.send_ctr + 1 ==
3211 				wsi->http.range.count_ranges && // last range
3212 			    wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
3213 			    wsi->http.range.budget - amount == 0) {// final part
3214 				n += lws_snprintf((char *)pstart + n, 6,
3215 					"_lws\x0d\x0a"); // append trailing boundary
3216 				lwsl_debug("added trailing boundary\n");
3217 			}
3218 #endif
3219 			m = lws_write(wsi, p, (unsigned int)n, wsi->http.filepos + amount ==
3220 					wsi->http.filelen ?
3221 					 LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
3222 			if (m < 0)
3223 				goto file_had_it;
3224 
3225 			wsi->http.filepos += amount;
3226 
3227 #if defined(LWS_WITH_RANGES)
3228 			if (wsi->http.range.count_ranges >= 1) {
3229 				wsi->http.range.budget -= amount;
3230 				if (wsi->http.range.budget == 0) {
3231 					lwsl_notice("range budget exhausted\n");
3232 					wsi->http.range.inside = 0;
3233 					wsi->http.range.send_ctr++;
3234 
3235 					if (lws_ranges_next(&wsi->http.range) < 1) {
3236 						finished = 1;
3237 						goto all_sent;
3238 					}
3239 				}
3240 			}
3241 #endif
3242 
3243 			if (m != n) {
3244 				/* adjust for what was not sent */
3245 				if (lws_vfs_file_seek_cur(wsi->http.fop_fd,
3246 							   m - n) ==
3247 							     (lws_fileofs_t)-1)
3248 					goto file_had_it;
3249 			}
3250 		}
3251 
3252 all_sent:
3253 		if ((!lws_has_buffered_out(wsi)
3254 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
3255 				&& !wsi->http.comp_ctx.buflist_comp &&
3256 		    !wsi->http.comp_ctx.may_have_more
3257 #endif
3258 		    ) && (wsi->http.filepos >= wsi->http.filelen
3259 #if defined(LWS_WITH_RANGES)
3260 		    || finished)
3261 #else
3262 		)
3263 #endif
3264 		) {
3265 			lwsi_set_state(wsi, LRS_ESTABLISHED);
3266 			/* we might be in keepalive, so close it off here */
3267 			lws_vfs_file_close(&wsi->http.fop_fd);
3268 
3269 			lwsl_debug("file completed\n");
3270 
3271 			if (wsi->a.protocol->callback &&
3272 			    user_callback_handle_rxflow(wsi->a.protocol->callback,
3273 					wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
3274 					wsi->user_space, NULL, 0) < 0) {
3275 					/*
3276 					 * For http/1.x, the choices from
3277 					 * transaction_completed are either
3278 					 * 0 to use the connection for pipelined
3279 					 * or nonzero to hang it up.
3280 					 *
3281 					 * However for http/2. while we are
3282 					 * still interested in hanging up the
3283 					 * nwsi if there was a network-level
3284 					 * fatal error, simply completing the
3285 					 * transaction is a matter of the stream
3286 					 * state, not the root connection at the
3287 					 * network level
3288 					 */
3289 					if (wsi->mux_substream)
3290 						return 1;
3291 					else
3292 						return -1;
3293 				}
3294 
3295 			return 1;  /* >0 indicates completed */
3296 		}
3297 		/*
3298 		 * while(1) here causes us to spam the whole file contents into
3299 		 * a hugely bloated output buffer if it ever can't send the
3300 		 * whole chunk...
3301 		 */
3302 	} while (!lws_send_pipe_choked(wsi));
3303 
3304 	lws_callback_on_writable(wsi);
3305 
3306 	return 0; /* indicates further processing must be done */
3307 
3308 file_had_it:
3309 	lws_vfs_file_close(&wsi->http.fop_fd);
3310 
3311 	return -1;
3312 }
3313 
3314 #endif
3315 
3316 #if defined(LWS_WITH_SERVER)
3317 void
lws_server_get_canonical_hostname(struct lws_context * context,const struct lws_context_creation_info * info)3318 lws_server_get_canonical_hostname(struct lws_context *context,
3319 				  const struct lws_context_creation_info *info)
3320 {
3321 	if (lws_check_opt(info->options,
3322 			LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
3323 		return;
3324 #if !defined(LWS_PLAT_FREERTOS)
3325 	/* find canonical hostname */
3326 	if (gethostname((char *)context->canonical_hostname,
3327 		        sizeof(context->canonical_hostname) - 1))
3328 		lws_strncpy((char *)context->canonical_hostname, "unknown",
3329 			    sizeof(context->canonical_hostname));
3330 
3331 	lwsl_cx_info(context, " canonical_hostname = %s\n",
3332 					context->canonical_hostname);
3333 #else
3334 	(void)context;
3335 #endif
3336 }
3337 #endif
3338 
3339 int
lws_chunked_html_process(struct lws_process_html_args * args,struct lws_process_html_state * s)3340 lws_chunked_html_process(struct lws_process_html_args *args,
3341 			 struct lws_process_html_state *s)
3342 {
3343 	char *sp, buffer[32];
3344 	const char *pc;
3345 	int old_len, n;
3346 
3347 	/* do replacements */
3348 	sp = args->p;
3349 	old_len = args->len;
3350 	args->len = 0;
3351 	s->start = sp;
3352 	while (sp < args->p + old_len) {
3353 
3354 		if (args->len + 7 >= args->max_len) {
3355 			lwsl_err("Used up interpret padding\n");
3356 			return -1;
3357 		}
3358 
3359 		if ((!s->pos && *sp == '$') || s->pos) {
3360 			int hits = 0, hit = 0;
3361 
3362 			if (!s->pos)
3363 				s->start = sp;
3364 			s->swallow[s->pos++] = *sp;
3365 			if (s->pos == sizeof(s->swallow) - 1)
3366 				goto skip;
3367 			for (n = 0; n < s->count_vars; n++)
3368 				if (!strncmp(s->swallow, s->vars[n], (unsigned int)s->pos)) {
3369 					hits++;
3370 					hit = n;
3371 				}
3372 			if (!hits) {
3373 skip:
3374 				s->swallow[s->pos] = '\0';
3375 				memcpy(s->start, s->swallow, (unsigned int)s->pos);
3376 				args->len++;
3377 				s->pos = 0;
3378 				sp = s->start + 1;
3379 				continue;
3380 			}
3381 			if (hits == 1 && s->pos == (int)strlen(s->vars[hit])) {
3382 				pc = s->replace(s->data, hit);
3383 				if (!pc)
3384 					pc = "NULL";
3385 				n = (int)strlen(pc);
3386 				s->swallow[s->pos] = '\0';
3387 				if (n != s->pos) {
3388 					memmove(s->start + n, s->start + s->pos,
3389 						(unsigned int)(old_len - (sp - args->p) - 1));
3390 					old_len += (n - s->pos) + 1;
3391 				}
3392 				memcpy(s->start, pc, (unsigned int)n);
3393 				args->len++;
3394 				sp = s->start + 1;
3395 
3396 				s->pos = 0;
3397 			}
3398 			sp++;
3399 			continue;
3400 		}
3401 
3402 		args->len++;
3403 		sp++;
3404 	}
3405 
3406 	if (args->chunked) {
3407 		/* no space left for final chunk trailer */
3408 		if (args->final && args->len + 7 >= args->max_len)
3409 			return -1;
3410 
3411 		n = sprintf(buffer, "%X\x0d\x0a", args->len);
3412 
3413 		args->p -= n;
3414 		memcpy(args->p, buffer, (unsigned int)n);
3415 		args->len += n;
3416 
3417 		if (args->final) {
3418 			sp = args->p + args->len;
3419 			*sp++ = '\x0d';
3420 			*sp++ = '\x0a';
3421 			*sp++ = '0';
3422 			*sp++ = '\x0d';
3423 			*sp++ = '\x0a';
3424 			*sp++ = '\x0d';
3425 			*sp++ = '\x0a';
3426 			args->len += 7;
3427 		} else {
3428 			sp = args->p + args->len;
3429 			*sp++ = '\x0d';
3430 			*sp++ = '\x0a';
3431 			args->len += 2;
3432 		}
3433 	}
3434 
3435 	return 0;
3436 }
3437