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