• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2021 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 void
28 lws_tls_session_vh_destroy(struct lws_vhost *vh);
29 
30 const struct lws_role_ops *available_roles[] = {
31 #if defined(LWS_ROLE_H2)
32 	&role_ops_h2,
33 #endif
34 #if defined(LWS_ROLE_H1)
35 	&role_ops_h1,
36 #endif
37 #if defined(LWS_ROLE_WS)
38 	&role_ops_ws,
39 #endif
40 #if defined(LWS_ROLE_DBUS)
41 	&role_ops_dbus,
42 #endif
43 #if defined(LWS_ROLE_RAW_PROXY)
44 	&role_ops_raw_proxy,
45 #endif
46 #if defined(LWS_ROLE_MQTT) && defined(LWS_WITH_CLIENT)
47 	&role_ops_mqtt,
48 #endif
49 #if defined(LWS_WITH_NETLINK)
50 	&role_ops_netlink,
51 #endif
52 	NULL
53 };
54 
55 #if defined(LWS_WITH_ABSTRACT)
56 const struct lws_protocols *available_abstract_protocols[] = {
57 #if defined(LWS_ROLE_RAW)
58 	&protocol_abs_client_raw_skt,
59 #endif
60 	NULL
61 };
62 #endif
63 
64 #if defined(LWS_WITH_SECURE_STREAMS)
65 const struct lws_protocols *available_secstream_protocols[] = {
66 #if defined(LWS_ROLE_H1)
67 	&protocol_secstream_h1,
68 #endif
69 #if defined(LWS_ROLE_H2)
70 	&protocol_secstream_h2,
71 #endif
72 #if defined(LWS_ROLE_WS)
73 	&protocol_secstream_ws,
74 #endif
75 #if defined(LWS_ROLE_MQTT)
76 	&protocol_secstream_mqtt,
77 #endif
78 	&protocol_secstream_raw,
79 	NULL
80 };
81 #endif
82 
83 static const char * const mount_protocols[] = {
84 	"http://",
85 	"https://",
86 	"file://",
87 	"cgi://",
88 	">http://",
89 	">https://",
90 	"callback://"
91 };
92 
93 const struct lws_role_ops *
lws_role_by_name(const char * name)94 lws_role_by_name(const char *name)
95 {
96 	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
97 		if (!strcmp(ar->name, name))
98 			return ar;
99 	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
100 
101 	if (!strcmp(name, role_ops_raw_skt.name))
102 		return &role_ops_raw_skt;
103 
104 #if defined(LWS_ROLE_RAW_FILE)
105 	if (!strcmp(name, role_ops_raw_file.name))
106 		return &role_ops_raw_file;
107 #endif
108 
109 	return NULL;
110 }
111 
112 int
lws_role_call_alpn_negotiated(struct lws * wsi,const char * alpn)113 lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)
114 {
115 #if defined(LWS_WITH_TLS)
116 	if (!alpn)
117 		return 0;
118 
119 #if !defined(LWS_ESP_PLATFORM)
120 	lwsl_wsi_info(wsi, "'%s'", alpn);
121 #endif
122 
123 	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
124 		if (ar->alpn && !strcmp(ar->alpn, alpn) &&
125 		    lws_rops_fidx(ar, LWS_ROPS_alpn_negotiated)) {
126 #if defined(LWS_WITH_SERVER)
127 			lws_metrics_tag_wsi_add(wsi, "upg", ar->name);
128 #endif
129 			return (lws_rops_func_fidx(ar, LWS_ROPS_alpn_negotiated)).
130 						   alpn_negotiated(wsi, alpn);
131 		}
132 	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
133 #endif
134 	return 0;
135 }
136 
137 int
lws_role_call_adoption_bind(struct lws * wsi,int type,const char * prot)138 lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot)
139 {
140 	int n;
141 
142 	/*
143 	 * if the vhost is told to bind accepted sockets to a given role,
144 	 * then look it up by name and try to bind to the specific role.
145 	 */
146 	if (lws_check_opt(wsi->a.vhost->options,
147 			  LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) &&
148 	    wsi->a.vhost->listen_accept_role) {
149 		const struct lws_role_ops *role =
150 			lws_role_by_name(wsi->a.vhost->listen_accept_role);
151 
152 		if (!prot)
153 			prot = wsi->a.vhost->listen_accept_protocol;
154 
155 		if (!role)
156 			lwsl_wsi_err(wsi, "can't find role '%s'",
157 					  wsi->a.vhost->listen_accept_role);
158 
159 		if (!strcmp(wsi->a.vhost->listen_accept_role, "raw-proxy"))
160 			type |= LWS_ADOPT_FLAG_RAW_PROXY;
161 
162 		if (role && lws_rops_fidx(role, LWS_ROPS_adoption_bind)) {
163 			n = (lws_rops_func_fidx(role, LWS_ROPS_adoption_bind)).
164 						adoption_bind(wsi, type, prot);
165 			if (n < 0)
166 				return -1;
167 			if (n) /* did the bind */
168 				return 0;
169 		}
170 
171 		if (type & _LWS_ADOPT_FINISH) {
172 			lwsl_wsi_debug(wsi, "leaving bound to role %s",
173 					    wsi->role_ops->name);
174 			return 0;
175 		}
176 
177 		lwsl_wsi_warn(wsi, "adoption bind to role '%s', "
178 			  "protocol '%s', type 0x%x, failed",
179 			  wsi->a.vhost->listen_accept_role, prot, type);
180 	}
181 
182 	/*
183 	 * Otherwise ask each of the roles in order of preference if they
184 	 * want to bind to this accepted socket
185 	 */
186 
187 	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
188 		if (lws_rops_fidx(ar, LWS_ROPS_adoption_bind) &&
189 		    (lws_rops_func_fidx(ar, LWS_ROPS_adoption_bind)).
190 					    adoption_bind(wsi, type, prot))
191 			return 0;
192 	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
193 
194 	/* fall back to raw socket role if, eg, h1 not configured */
195 
196 	if (lws_rops_fidx(&role_ops_raw_skt, LWS_ROPS_adoption_bind) &&
197 	    (lws_rops_func_fidx(&role_ops_raw_skt, LWS_ROPS_adoption_bind)).
198 				    adoption_bind(wsi, type, prot))
199 		return 0;
200 
201 #if defined(LWS_ROLE_RAW_FILE)
202 
203 	lwsl_wsi_notice(wsi, "falling back to raw file role bind");
204 
205 	/* fall back to raw file role if, eg, h1 not configured */
206 
207 	if (lws_rops_fidx(&role_ops_raw_file, LWS_ROPS_adoption_bind) &&
208 	    (lws_rops_func_fidx(&role_ops_raw_file, LWS_ROPS_adoption_bind)).
209 				    adoption_bind(wsi, type, prot))
210 		return 0;
211 #endif
212 
213 	return 1;
214 }
215 
216 #if defined(LWS_WITH_CLIENT)
217 int
lws_role_call_client_bind(struct lws * wsi,const struct lws_client_connect_info * i)218 lws_role_call_client_bind(struct lws *wsi,
219 			  const struct lws_client_connect_info *i)
220 {
221 	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
222 		if (lws_rops_fidx(ar, LWS_ROPS_client_bind)) {
223 			int m = (lws_rops_func_fidx(ar, LWS_ROPS_client_bind)).
224 							client_bind(wsi, i);
225 
226 			if (m < 0)
227 				return m;
228 			if (m)
229 				return 0;
230 		}
231 	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
232 
233 	/* fall back to raw socket role if, eg, h1 not configured */
234 
235 	if (lws_rops_fidx(&role_ops_raw_skt, LWS_ROPS_client_bind) &&
236 	    (lws_rops_func_fidx(&role_ops_raw_skt, LWS_ROPS_client_bind)).
237 					client_bind(wsi, i))
238 		return 0;
239 
240 	return 1;
241 }
242 #endif
243 
244 void *
lws_protocol_vh_priv_zalloc(struct lws_vhost * vhost,const struct lws_protocols * prot,int size)245 lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost,
246 			    const struct lws_protocols *prot, int size)
247 {
248 	int n = 0;
249 
250 	if (!vhost || !prot || !vhost->protocols || !prot->name)
251 		return NULL;
252 
253 	/* allocate the vh priv array only on demand */
254 	if (!vhost->protocol_vh_privs) {
255 		vhost->protocol_vh_privs = (void **)lws_zalloc(
256 				(size_t)vhost->count_protocols * sizeof(void *),
257 				"protocol_vh_privs");
258 
259 		if (!vhost->protocol_vh_privs)
260 			return NULL;
261 	}
262 
263 	while (n < vhost->count_protocols && &vhost->protocols[n] != prot)
264 		n++;
265 
266 	if (n == vhost->count_protocols) {
267 		n = 0;
268 		while (n < vhost->count_protocols) {
269 			if (vhost->protocols[n].name &&
270 			    !strcmp(vhost->protocols[n].name, prot->name))
271 				break;
272 			n++;
273 		}
274 
275 		if (n == vhost->count_protocols) {
276 			lwsl_vhost_err(vhost, "unknown protocol %p", prot);
277 			return NULL;
278 		}
279 	}
280 
281 	vhost->protocol_vh_privs[n] = lws_zalloc((size_t)size, "vh priv");
282 	return vhost->protocol_vh_privs[n];
283 }
284 
285 void *
lws_protocol_vh_priv_get(struct lws_vhost * vhost,const struct lws_protocols * prot)286 lws_protocol_vh_priv_get(struct lws_vhost *vhost,
287 			 const struct lws_protocols *prot)
288 {
289 	int n = 0;
290 
291 	if (!vhost || !vhost->protocols ||
292 	    !vhost->protocol_vh_privs || !prot || !prot->name)
293 		return NULL;
294 
295 	while (n < vhost->count_protocols && &vhost->protocols[n] != prot)
296 		n++;
297 
298 	if (n == vhost->count_protocols) {
299 		n = 0;
300 		while (n < vhost->count_protocols) {
301 			if (vhost->protocols[n].name &&
302 			    !strcmp(vhost->protocols[n].name, prot->name))
303 				break;
304 			n++;
305 		}
306 
307 		if (n == vhost->count_protocols) {
308 			lwsl_vhost_err(vhost, "unknown protocol %p", prot);
309 			return NULL;
310 		}
311 	}
312 
313 	return vhost->protocol_vh_privs[n];
314 }
315 
316 void *
lws_vhd_find_by_pvo(struct lws_context * cx,const char * protname,const char * pvo_name,const char * pvo_value)317 lws_vhd_find_by_pvo(struct lws_context *cx, const char *protname,
318 		    const char *pvo_name, const char *pvo_value)
319 {
320 	struct lws_vhost *vh;
321 	int n;
322 
323 	/* let's go through all the vhosts */
324 
325 	vh = cx->vhost_list;
326 	while (vh) {
327 
328 		if (vh->protocol_vh_privs) {
329 
330 		for (n = 0; n < vh->count_protocols; n++) {
331 			const struct lws_protocol_vhost_options *pv;
332 
333 			if (strcmp(vh->protocols[n].name, protname))
334 				continue;
335 
336 			/* this vh has an instance of the required protocol */
337 
338 			pv = lws_pvo_search(vh->pvo, protname);
339 			if (!pv)
340 				continue;
341 
342 			pv = lws_pvo_search(pv->options, pvo_name);
343 			if (!pv)
344 				continue;
345 
346 			/* ... he also has a pvo of the right name... */
347 			if (!strcmp(pv->value, pvo_value))
348 				/*
349 				 * ... yes, the pvo has the right value too,
350 				 * return a pointer to this vhost-protocol
351 				 * private alloc (ie, its "vhd")
352 				 */
353 				return vh->protocol_vh_privs[n];
354 		}
355 		} else
356 			lwsl_vhost_notice(vh, "no privs yet");
357 		vh = vh->vhost_next;
358 	}
359 
360 	return NULL;
361 }
362 
363 const struct lws_protocol_vhost_options *
lws_vhost_protocol_options(struct lws_vhost * vh,const char * name)364 lws_vhost_protocol_options(struct lws_vhost *vh, const char *name)
365 {
366 	const struct lws_protocol_vhost_options *pvo = vh->pvo;
367 
368 	if (!name)
369 		return NULL;
370 
371 	while (pvo) {
372 		if (!strcmp(pvo->name, name))
373 			return pvo;
374 		pvo = pvo->next;
375 	}
376 
377 	return NULL;
378 }
379 
380 int
lws_protocol_init_vhost(struct lws_vhost * vh,int * any)381 lws_protocol_init_vhost(struct lws_vhost *vh, int *any)
382 {
383 	const struct lws_protocol_vhost_options *pvo, *pvo1;
384 	int n;
385 #if defined(LWS_PLAT_FREERTOS)
386 	struct lws_a _lwsa, *lwsa = &_lwsa;
387 
388 	memset(&_lwsa, 0, sizeof(_lwsa));
389 #else
390 	struct lws _lws;
391 	struct lws_a *lwsa = &_lws.a;
392 
393 	memset(&_lws, 0, sizeof(_lws));
394 #endif
395 
396 	lwsa->context = vh->context;
397 	lwsa->vhost = vh;
398 
399 	/* initialize supported protocols on this vhost */
400 
401 	for (n = 0; n < vh->count_protocols; n++) {
402 		lwsa->protocol = &vh->protocols[n];
403 		if (!vh->protocols[n].name)
404 			continue;
405 		pvo = lws_vhost_protocol_options(vh, vh->protocols[n].name);
406 		if (pvo) {
407 			/*
408 			 * linked list of options specific to
409 			 * vh + protocol
410 			 */
411 			pvo1 = pvo;
412 			pvo = pvo1->options;
413 
414 			while (pvo) {
415 				lwsl_vhost_debug(vh, "protocol \"%s\", "
416 						     "option \"%s\"",
417 						     vh->protocols[n].name,
418 						     pvo->name);
419 
420 				if (!strcmp(pvo->name, "default")) {
421 					lwsl_vhost_info(vh, "Setting default "
422 							     "protocol to %s",
423 							     vh->protocols[n].name);
424 					vh->default_protocol_index = (unsigned char)n;
425 				}
426 				if (!strcmp(pvo->name, "raw")) {
427 					lwsl_vhost_info(vh, "Setting raw "
428 							     "protocol to %s",
429 							     vh->protocols[n].name);
430 					vh->raw_protocol_index = (unsigned char)n;
431 				}
432 				pvo = pvo->next;
433 			}
434 		} else
435 			lwsl_vhost_debug(vh, "not instantiating %s",
436 					     vh->protocols[n].name);
437 
438 #if defined(LWS_WITH_TLS)
439 		if (any)
440 			*any |= !!vh->tls.ssl_ctx;
441 #endif
442 
443 		pvo = lws_vhost_protocol_options(vh, vh->protocols[n].name);
444 
445 		/*
446 		 * inform all the protocols that they are doing their
447 		 * one-time initialization if they want to.
448 		 *
449 		 * NOTE the fakewsi is garbage, except the key pointers that are
450 		 * prepared in case the protocol handler wants to touch them
451 		 */
452 
453 		if (pvo
454 #if !defined(LWS_WITH_PLUGINS)
455 				/*
456 				 * with plugins, you have to explicitly
457 				 * instantiate them per-vhost with pvos.
458 				 *
459 				 * Without plugins, not setting the vhost pvo
460 				 * list at creation enables all the protocols
461 				 * by default, for backwards compatibility
462 				 */
463 				|| !vh->pvo
464 #endif
465 		) {
466 			lwsl_vhost_info(vh, "init %s.%s", vh->name,
467 					vh->protocols[n].name);
468 			if (vh->protocols[n].callback((struct lws *)lwsa,
469 				LWS_CALLBACK_PROTOCOL_INIT, NULL,
470 #if !defined(LWS_WITH_PLUGINS)
471 				(void *)(pvo ? pvo->options : NULL),
472 #else
473 				(void *)pvo->options,
474 #endif
475 				0)) {
476 				if (vh->protocol_vh_privs && vh->protocol_vh_privs[n]) {
477 					lws_free(vh->protocol_vh_privs[n]);
478 					vh->protocol_vh_privs[n] = NULL;
479 				}
480 			lwsl_vhost_err(vh, "protocol %s failed init",
481 					vh->protocols[n].name);
482 
483 				return 1;
484 			}
485 		}
486 	}
487 
488 	vh->created_vhost_protocols = 1;
489 
490 	return 0;
491 }
492 
493 /*
494  * inform every vhost that hasn't already done it, that
495  * his protocols are initializing
496  */
497 int
lws_protocol_init(struct lws_context * context)498 lws_protocol_init(struct lws_context *context)
499 {
500 	struct lws_vhost *vh = context->vhost_list;
501 	int any = 0, r = 0;
502 
503 	if (context->doing_protocol_init)
504 		return 0;
505 
506 	context->doing_protocol_init = 1;
507 
508 	lwsl_cx_info(context, "\n");
509 
510 	while (vh) {
511 
512 		/* only do the protocol init once for a given vhost */
513 		if (vh->created_vhost_protocols ||
514 		    (lws_check_opt(vh->options, LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT)))
515 			goto next;
516 
517 		if (lws_protocol_init_vhost(vh, &any)) {
518 			lwsl_vhost_warn(vh, "init vhost %s failed", vh->name);
519 			r = -1;
520 		}
521 next:
522 		vh = vh->vhost_next;
523 	}
524 
525 	context->doing_protocol_init = 0;
526 
527 	if (r)
528 		lwsl_cx_warn(context, "some protocols did not init");
529 
530 	if (!context->protocol_init_done) {
531 
532 		context->protocol_init_done = 1;
533 		lws_finalize_startup(context);
534 
535 		return 0;
536 	}
537 
538 #if defined(LWS_WITH_SERVER)
539 	if (any) {
540 		lws_tls_check_all_cert_lifetimes(context);
541 	}
542 #endif
543 
544 	return 0;
545 }
546 
547 
548 /* list of supported protocols and callbacks */
549 
550 static const struct lws_protocols protocols_dummy[] = {
551 	/* first protocol must always be HTTP handler */
552 
553 	{
554 		"http-only",			/* name */
555 		lws_callback_http_dummy,	/* callback */
556 		0,				/* per_session_data_size */
557 		0,				/* rx_buffer_size */
558 		0,				/* id */
559 		NULL,				/* user */
560 		0				/* tx_packet_size */
561 	},
562 	/*
563 	 * the other protocols are provided by lws plugins
564 	 */
565 	{ NULL, NULL, 0, 0, 0, NULL, 0} /* terminator */
566 };
567 
568 
569 #ifdef LWS_PLAT_OPTEE
570 #undef LWS_HAVE_GETENV
571 #endif
572 
573 struct lws_vhost *
lws_create_vhost(struct lws_context * context,const struct lws_context_creation_info * info)574 lws_create_vhost(struct lws_context *context,
575 		 const struct lws_context_creation_info *info)
576 {
577 	struct lws_vhost *vh, **vh1 = &context->vhost_list;
578 	const struct lws_http_mount *mounts;
579 	const struct lws_protocols *pcols = info->protocols;
580 #ifdef LWS_WITH_PLUGINS
581 	struct lws_plugin *plugin = context->plugin_list;
582 #endif
583 	struct lws_protocols *lwsp;
584 	int m, f = !info->pvo, fx = 0, abs_pcol_count = 0, sec_pcol_count = 0;
585 	const char *name = "default";
586 	char buf[96];
587 	char *p;
588 #if defined(LWS_WITH_SYS_ASYNC_DNS)
589 	extern struct lws_protocols lws_async_dns_protocol;
590 #endif
591 	int n;
592 
593 	if (info->vhost_name)
594 		name = info->vhost_name;
595 
596 	if (lws_fi(&info->fic, "vh_create_oom"))
597 		vh = NULL;
598 	else
599 		vh = lws_zalloc(sizeof(*vh) + strlen(name) + 1
600 #if defined(LWS_WITH_EVENT_LIBS)
601 			+ context->event_loop_ops->evlib_size_vh
602 #endif
603 			, __func__);
604 	if (!vh)
605 		goto early_bail;
606 
607 	if (info->log_cx)
608 		vh->lc.log_cx = info->log_cx;
609 	else
610 		vh->lc.log_cx = &log_cx;
611 
612 #if defined(LWS_WITH_EVENT_LIBS)
613 	vh->evlib_vh = (void *)&vh[1];
614 	vh->name = (const char *)vh->evlib_vh +
615 			context->event_loop_ops->evlib_size_vh;
616 #else
617 	vh->name = (const char *)&vh[1];
618 #endif
619 	memcpy((char *)vh->name, name, strlen(name) + 1);
620 
621 #if LWS_MAX_SMP > 1
622 	lws_mutex_refcount_init(&vh->mr);
623 #endif
624 
625 	if (!pcols && !info->pprotocols)
626 		pcols = &protocols_dummy[0];
627 
628 	vh->context = context;
629 	{
630 		char *end = buf + sizeof(buf) - 1;
631 		p = buf;
632 
633 		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s", vh->name);
634 		if (info->iface)
635 			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "|%s", info->iface);
636 		if (info->port && !(info->port & 0xffff))
637 			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "|%u", info->port);
638 	}
639 
640 	__lws_lc_tag(context, &context->lcg[LWSLCG_VHOST], &vh->lc, "%s|%s|%d",
641 		     buf, info->iface ? info->iface : "", info->port);
642 
643 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
644 	vh->fic.name = "vh";
645 	if (info->fic.fi_owner.count)
646 		/*
647 		 * This moves all the lws_fi_t from info->fi to the vhost fi,
648 		 * leaving it empty
649 		 */
650 		lws_fi_import(&vh->fic, &info->fic);
651 
652 	lws_fi_inherit_copy(&vh->fic, &context->fic, "vh", vh->name);
653 	if (lws_fi(&vh->fic, "vh_create_oom"))
654 		goto bail;
655 #endif
656 
657 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
658 	vh->http.error_document_404 = info->error_document_404;
659 #endif
660 
661 	if (lws_check_opt(info->options, LWS_SERVER_OPTION_ONLY_RAW))
662 		lwsl_vhost_info(vh, "set to only support RAW");
663 
664 	vh->iface = info->iface;
665 #if !defined(LWS_PLAT_FREERTOS) && !defined(OPTEE_TA) && !defined(WIN32)
666 	vh->bind_iface = info->bind_iface;
667 #endif
668 #if defined(LWS_WITH_CLIENT)
669 	if (info->connect_timeout_secs)
670 		vh->connect_timeout_secs = (int)info->connect_timeout_secs;
671 	else
672 		vh->connect_timeout_secs = 20;
673 #endif
674 	/* apply the context default lws_retry */
675 
676 	if (info->retry_and_idle_policy)
677 		vh->retry_policy = info->retry_and_idle_policy;
678 	else
679 		vh->retry_policy = &context->default_retry;
680 
681 	/*
682 	 * let's figure out how many protocols the user is handing us, using the
683 	 * old or new way depending on what he gave us
684 	 */
685 
686 	if (!pcols)
687 		for (vh->count_protocols = 0;
688 			info->pprotocols[vh->count_protocols];
689 			vh->count_protocols++)
690 			;
691 	else
692 		for (vh->count_protocols = 0;
693 			pcols[vh->count_protocols].callback;
694 			vh->count_protocols++)
695 				;
696 
697 	vh->options			= info->options;
698 	vh->pvo				= info->pvo;
699 	vh->headers			= info->headers;
700 	vh->user			= info->user;
701 	vh->finalize			= info->finalize;
702 	vh->finalize_arg		= info->finalize_arg;
703 	vh->listen_accept_role		= info->listen_accept_role;
704 	vh->listen_accept_protocol	= info->listen_accept_protocol;
705 	vh->unix_socket_perms		= info->unix_socket_perms;
706 	vh->fo_listen_queue		= info->fo_listen_queue;
707 
708 	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
709 	if (lws_rops_fidx(ar, LWS_ROPS_init_vhost) &&
710 	    (lws_rops_func_fidx(ar, LWS_ROPS_init_vhost)).init_vhost(vh, info))
711 		return NULL;
712 	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
713 
714 
715 	if (info->keepalive_timeout)
716 		vh->keepalive_timeout = info->keepalive_timeout;
717 	else
718 		vh->keepalive_timeout = 5;
719 
720 	if (info->timeout_secs_ah_idle)
721 		vh->timeout_secs_ah_idle = (int)info->timeout_secs_ah_idle;
722 	else
723 		vh->timeout_secs_ah_idle = 10;
724 
725 #if defined(LWS_WITH_TLS)
726 
727 	vh->tls.alpn = info->alpn;
728 	vh->tls.ssl_info_event_mask = info->ssl_info_event_mask;
729 
730 	if (info->ecdh_curve)
731 		lws_strncpy(vh->tls.ecdh_curve, info->ecdh_curve,
732 			    sizeof(vh->tls.ecdh_curve));
733 
734 	/* carefully allocate and take a copy of cert + key paths if present */
735 	n = 0;
736 	if (info->ssl_cert_filepath)
737 		n += (int)strlen(info->ssl_cert_filepath) + 1;
738 	if (info->ssl_private_key_filepath)
739 		n += (int)strlen(info->ssl_private_key_filepath) + 1;
740 
741 	if (n) {
742 		vh->tls.key_path = vh->tls.alloc_cert_path =
743 					lws_malloc((unsigned int)n, "vh paths");
744 		if (info->ssl_cert_filepath) {
745 			n = (int)strlen(info->ssl_cert_filepath) + 1;
746 			memcpy(vh->tls.alloc_cert_path,
747 			       info->ssl_cert_filepath, (unsigned int)n);
748 			vh->tls.key_path += n;
749 		}
750 		if (info->ssl_private_key_filepath)
751 			memcpy(vh->tls.key_path, info->ssl_private_key_filepath,
752 			       strlen(info->ssl_private_key_filepath) + 1);
753 	}
754 #endif
755 
756 #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
757 	fx = 1;
758 #endif
759 #if defined(LWS_WITH_ABSTRACT)
760 	abs_pcol_count = (int)LWS_ARRAY_SIZE(available_abstract_protocols) - 1;
761 #endif
762 #if defined(LWS_WITH_SECURE_STREAMS)
763 	sec_pcol_count = (int)LWS_ARRAY_SIZE(available_secstream_protocols) - 1;
764 #endif
765 
766 	/*
767 	 * give the vhost a unified list of protocols including:
768 	 *
769 	 * - internal, async_dns if enabled (first vhost only)
770 	 * - internal, abstracted ones
771 	 * - the ones that came from plugins
772 	 * - his user protocols
773 	 */
774 
775 	if (lws_fi(&vh->fic, "vh_create_pcols_oom"))
776 		lwsp = NULL;
777 	else
778 		lwsp = lws_zalloc(sizeof(struct lws_protocols) *
779 				((unsigned int)vh->count_protocols +
780 				   (unsigned int)abs_pcol_count +
781 				   (unsigned int)sec_pcol_count +
782 				   (unsigned int)context->plugin_protocol_count +
783 				   (unsigned int)fx + 1), "vh plugin table");
784 	if (!lwsp) {
785 		lwsl_err("OOM\n");
786 		goto bail;
787 	}
788 
789 	/*
790 	 * 1: user protocols (from pprotocols or protocols)
791 	 */
792 
793 	m = vh->count_protocols;
794 	if (!pcols) {
795 		for (n = 0; n < m; n++)
796 			memcpy(&lwsp[n], info->pprotocols[n], sizeof(lwsp[0]));
797 	} else
798 		memcpy(lwsp, pcols, sizeof(struct lws_protocols) * (unsigned int)m);
799 
800 	/*
801 	 * 2: abstract protocols
802 	 */
803 #if defined(LWS_WITH_ABSTRACT)
804 	for (n = 0; n < abs_pcol_count; n++) {
805 		memcpy(&lwsp[m++], available_abstract_protocols[n],
806 		       sizeof(*lwsp));
807 		vh->count_protocols++;
808 	}
809 #endif
810 	/*
811 	 * 3: async dns protocol (first vhost only)
812 	 */
813 #if defined(LWS_WITH_SYS_ASYNC_DNS)
814 	if (!context->vhost_list) {
815 		memcpy(&lwsp[m++], &lws_async_dns_protocol,
816 		       sizeof(struct lws_protocols));
817 		vh->count_protocols++;
818 	}
819 #endif
820 
821 #if defined(LWS_WITH_SECURE_STREAMS)
822 	for (n = 0; n < sec_pcol_count; n++) {
823 		memcpy(&lwsp[m++], available_secstream_protocols[n],
824 		       sizeof(*lwsp));
825 		vh->count_protocols++;
826 	}
827 #endif
828 
829 	/*
830 	 * 3: For compatibility, all protocols enabled on vhost if only
831 	 * the default vhost exists.  Otherwise only vhosts who ask
832 	 * for a protocol get it enabled.
833 	 */
834 
835 	if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)
836 		f = 0;
837 	(void)f;
838 #ifdef LWS_WITH_PLUGINS
839 	if (plugin) {
840 		while (plugin) {
841 			const lws_plugin_protocol_t *plpr =
842 				(const lws_plugin_protocol_t *)plugin->hdr;
843 
844 			for (n = 0; n < plpr->count_protocols; n++) {
845 				/*
846 				 * for compatibility's sake, no pvo implies
847 				 * allow all protocols
848 				 */
849 				if (f || lws_vhost_protocol_options(vh,
850 						plpr->protocols[n].name)) {
851 					memcpy(&lwsp[m],
852 					       &plpr->protocols[n],
853 					       sizeof(struct lws_protocols));
854 					m++;
855 					vh->count_protocols++;
856 				}
857 			}
858 			plugin = plugin->list;
859 		}
860 	}
861 #endif
862 
863 #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
864 	memcpy(&lwsp[m++], &lws_ws_proxy, sizeof(*lwsp));
865 	vh->count_protocols++;
866 #endif
867 
868 	vh->protocols = lwsp;
869 	vh->allocated_vhost_protocols = 1;
870 
871 	vh->same_vh_protocol_owner = (struct lws_dll2_owner *)
872 			lws_zalloc(sizeof(struct lws_dll2_owner) *
873 				   (unsigned int)vh->count_protocols, "same vh list");
874 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
875 	vh->http.mount_list = info->mounts;
876 #endif
877 
878 #if defined(LWS_WITH_SYS_METRICS) && defined(LWS_WITH_SERVER)
879 	{
880 		char *end = buf + sizeof(buf) - 1;
881 		p = buf;
882 
883 		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "vh.%s", vh->name);
884 		if (info->iface)
885 			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ".%s", info->iface);
886 		if (info->port && !(info->port & 0xffff))
887 			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ".%u", info->port);
888 		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ".rx");
889 		vh->mt_traffic_rx = lws_metric_create(context, 0, buf);
890 		p[-2] = 't';
891 		vh->mt_traffic_tx = lws_metric_create(context, 0, buf);
892 	}
893 #endif
894 
895 #ifdef LWS_WITH_UNIX_SOCK
896 	if (LWS_UNIX_SOCK_ENABLED(vh)) {
897 		lwsl_vhost_info(vh, "Creating '%s' path \"%s\", %d protocols",
898 				vh->name, vh->iface, vh->count_protocols);
899 	} else
900 #endif
901 	{
902 		switch(info->port) {
903 		case CONTEXT_PORT_NO_LISTEN:
904 			strcpy(buf, "(serving disabled)");
905 			break;
906 		case CONTEXT_PORT_NO_LISTEN_SERVER:
907 			strcpy(buf, "(no listener)");
908 			break;
909 		default:
910 			lws_snprintf(buf, sizeof(buf), "port %u", info->port);
911 			break;
912 		}
913 		lwsl_vhost_info(vh, "Creating Vhost '%s' %s, %d protocols, IPv6 %s",
914 			    vh->name, buf, vh->count_protocols,
915 			    LWS_IPV6_ENABLED(vh) ? "on" : "off");
916 	}
917 	mounts = info->mounts;
918 	while (mounts) {
919 		(void)mount_protocols[0];
920 		lwsl_vhost_info(vh, "   mounting %s%s to %s",
921 			  mount_protocols[mounts->origin_protocol],
922 			  mounts->origin ? mounts->origin : "none",
923 			  mounts->mountpoint);
924 
925 		mounts = mounts->mount_next;
926 	}
927 
928 	vh->listen_port = info->port;
929 
930 #if defined(LWS_WITH_SOCKS5)
931 	vh->socks_proxy_port = 0;
932 	vh->socks_proxy_address[0] = '\0';
933 #endif
934 
935 #if defined(LWS_WITH_CLIENT) && defined(LWS_CLIENT_HTTP_PROXYING)
936 	/* either use proxy from info, or try get it from env var */
937 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
938 	vh->http.http_proxy_port = 0;
939 	vh->http.http_proxy_address[0] = '\0';
940 	/* http proxy */
941 	if (info->http_proxy_address) {
942 		/* override for backwards compatibility */
943 		if (info->http_proxy_port)
944 			vh->http.http_proxy_port = info->http_proxy_port;
945 		lws_set_proxy(vh, info->http_proxy_address);
946 	} else
947 #endif
948 	{
949 #ifdef LWS_HAVE_GETENV
950 #if defined(__COVERITY__)
951 		p = NULL;
952 #else
953 		p = getenv("http_proxy"); /* coverity[tainted_scalar] */
954 		if (p) {
955 			lws_strncpy(buf, p, sizeof(buf));
956 			lws_set_proxy(vh, buf);
957 		}
958 #endif
959 #endif
960 	}
961 #endif
962 #if defined(LWS_WITH_SOCKS5)
963 	lws_socks5c_ads_server(vh, info);
964 #endif
965 
966 	vh->ka_time = info->ka_time;
967 	vh->ka_interval = info->ka_interval;
968 	vh->ka_probes = info->ka_probes;
969 
970 	if (vh->options & LWS_SERVER_OPTION_STS)
971 		lwsl_vhost_notice(vh, "   STS enabled");
972 
973 #ifdef LWS_WITH_ACCESS_LOG
974 	if (info->log_filepath) {
975 		if (lws_fi(&vh->fic, "vh_create_access_log_open_fail"))
976 			vh->log_fd = (int)LWS_INVALID_FILE;
977 		else
978 			vh->log_fd = lws_open(info->log_filepath,
979 				  O_CREAT | O_APPEND | O_RDWR, 0600);
980 		if (vh->log_fd == (int)LWS_INVALID_FILE) {
981 			lwsl_vhost_err(vh, "unable to open log filepath %s",
982 					   info->log_filepath);
983 			goto bail;
984 		}
985 #ifndef WIN32
986 		if (context->uid != (uid_t)-1)
987 			if (chown(info->log_filepath, context->uid,
988 				  context->gid) == -1)
989 				lwsl_vhost_err(vh, "unable to chown log file %s",
990 						   info->log_filepath);
991 #endif
992 	} else
993 		vh->log_fd = (int)LWS_INVALID_FILE;
994 #endif
995 	if (lws_fi(&vh->fic, "vh_create_ssl_srv") ||
996 	    lws_context_init_server_ssl(info, vh)) {
997 		lwsl_vhost_err(vh, "lws_context_init_server_ssl failed");
998 		goto bail1;
999 	}
1000 	if (lws_fi(&vh->fic, "vh_create_ssl_cli") ||
1001 	    lws_context_init_client_ssl(info, vh)) {
1002 		lwsl_vhost_err(vh, "lws_context_init_client_ssl failed");
1003 		goto bail1;
1004 	}
1005 #if defined(LWS_WITH_SERVER)
1006 	lws_context_lock(context, __func__);
1007 	if (lws_fi(&vh->fic, "vh_create_srv_init"))
1008 		n = -1;
1009 	else
1010 		n = _lws_vhost_init_server(info, vh);
1011 	lws_context_unlock(context);
1012 	if (n < 0) {
1013 		lwsl_vhost_err(vh, "init server failed\n");
1014 		goto bail1;
1015 	}
1016 #endif
1017 
1018 #if defined(LWS_WITH_SYS_ASYNC_DNS)
1019 	n = !!context->vhost_list;
1020 #endif
1021 
1022 	while (1) {
1023 		if (!(*vh1)) {
1024 			*vh1 = vh;
1025 			break;
1026 		}
1027 		vh1 = &(*vh1)->vhost_next;
1028 	};
1029 
1030 #if defined(LWS_WITH_SYS_ASYNC_DNS)
1031 	if (!n)
1032 		lws_async_dns_init(context);
1033 #endif
1034 
1035 	/* for the case we are adding a vhost much later, after server init */
1036 
1037 	if (context->protocol_init_done)
1038 		if (lws_fi(&vh->fic, "vh_create_protocol_init") ||
1039 		    lws_protocol_init(context)) {
1040 			lwsl_vhost_err(vh, "lws_protocol_init failed");
1041 			goto bail1;
1042 		}
1043 
1044 	return vh;
1045 
1046 bail1:
1047 	lws_vhost_destroy(vh);
1048 
1049 	return NULL;
1050 
1051 bail:
1052 	__lws_lc_untag(vh->context, &vh->lc);
1053 	lws_fi_destroy(&vh->fic);
1054 	lws_free(vh);
1055 
1056 early_bail:
1057 	lws_fi_destroy(&info->fic);
1058 
1059 	return NULL;
1060 }
1061 
1062 int
lws_init_vhost_client_ssl(const struct lws_context_creation_info * info,struct lws_vhost * vhost)1063 lws_init_vhost_client_ssl(const struct lws_context_creation_info *info,
1064 			  struct lws_vhost *vhost)
1065 {
1066 	struct lws_context_creation_info i;
1067 
1068 	memcpy(&i, info, sizeof(i));
1069 	i.port = CONTEXT_PORT_NO_LISTEN;
1070 
1071 	return lws_context_init_client_ssl(&i, vhost);
1072 }
1073 
1074 void
lws_cancel_service_pt(struct lws * wsi)1075 lws_cancel_service_pt(struct lws *wsi)
1076 {
1077 	lws_plat_pipe_signal(wsi->a.context, wsi->tsi);
1078 }
1079 
1080 void
lws_cancel_service(struct lws_context * context)1081 lws_cancel_service(struct lws_context *context)
1082 {
1083 	struct lws_context_per_thread *pt = &context->pt[0];
1084 	short m;
1085 
1086 	if (context->service_no_longer_possible)
1087 		return;
1088 
1089 	lwsl_cx_debug(context, "\n");
1090 
1091 	for (m = 0; m < context->count_threads; m++) {
1092 		if (pt->pipe_wsi)
1093 			lws_plat_pipe_signal(pt->context, m);
1094 		pt++;
1095 	}
1096 }
1097 
1098 int
__lws_create_event_pipes(struct lws_context * context)1099 __lws_create_event_pipes(struct lws_context *context)
1100 {
1101 	struct lws_context_per_thread *pt;
1102 	struct lws *wsi;
1103 	int n;
1104 
1105 	/*
1106 	 * Create the pt event pipes... these are unique in that they are
1107 	 * not bound to a vhost or protocol (both are NULL)
1108 	 */
1109 
1110 #if LWS_MAX_SMP > 1
1111 	for (n = 0; n < context->count_threads; n++) {
1112 #else
1113 	n = 0;
1114 	{
1115 #endif
1116 		pt = &context->pt[n];
1117 
1118 		if (pt->pipe_wsi)
1119 			return 0;
1120 
1121 		wsi = __lws_wsi_create_with_role(context, n, &role_ops_pipe,
1122 							NULL);
1123 		if (!wsi)
1124 			return 1;
1125 
1126 		__lws_lc_tag(context, &context->lcg[LWSLCG_WSI], &wsi->lc,
1127 				"pipe");
1128 
1129 		wsi->event_pipe = 1;
1130 		pt->pipe_wsi = wsi;
1131 
1132 		if (!lws_plat_pipe_create(wsi)) {
1133 			/*
1134 			 * platform code returns 0 if it actually created pipes
1135 			 * and initialized pt->dummy_pipe_fds[].  If it used
1136 			 * some other mechanism outside of signaling in the
1137 			 * normal event loop, we skip treating the pipe as
1138 			 * related to dummy_pipe_fds[], adding it to the fds,
1139 			 * etc.
1140 			 */
1141 
1142 			wsi->desc.sockfd = context->pt[n].dummy_pipe_fds[0];
1143 			// lwsl_debug("event pipe fd %d\n", wsi->desc.sockfd);
1144 
1145 			if (lws_wsi_inject_to_loop(pt, wsi))
1146 					goto bail;
1147 		}
1148 	}
1149 
1150 	return 0;
1151 
1152 bail:
1153 
1154 	return 1;
1155 }
1156 
1157 void
1158 lws_destroy_event_pipe(struct lws *wsi)
1159 {
1160 	int n;
1161 
1162 	lwsl_wsi_info(wsi, "in");
1163 
1164 	n = lws_wsi_extract_from_loop(wsi);
1165 	lws_plat_pipe_close(wsi);
1166 	if (!n)
1167 		lws_free(wsi);
1168 }
1169 
1170 /*
1171  * Start close process for any wsi bound to this vhost that belong to the
1172  * service thread we are called from.  Because of async event lib close, or
1173  * protocol staged close on wsi, latency with pts joining in closing their
1174  * wsi on the vhost, this may take some time.
1175  *
1176  * When the wsi count bound to the vhost (from all pts) drops to zero, the
1177  * vhost destruction will be finalized.
1178  */
1179 
1180 void
1181 __lws_vhost_destroy_pt_wsi_dieback_start(struct lws_vhost *vh)
1182 {
1183 #if LWS_MAX_SMP > 1
1184 	/* calling pt thread has done its wsi dieback */
1185 	int tsi = lws_pthread_self_to_tsi(vh->context);
1186 #else
1187 	int tsi = 0;
1188 #endif
1189 	struct lws_context *ctx = vh->context;
1190 	struct lws_context_per_thread *pt = &ctx->pt[tsi];
1191 	unsigned int n;
1192 
1193 #if LWS_MAX_SMP > 1
1194 	if (vh->close_flow_vs_tsi[lws_pthread_self_to_tsi(vh->context)])
1195 		/* this pt has already done its bit */
1196 		return;
1197 #endif
1198 
1199 #if defined(LWS_WITH_CLIENT)
1200 	/*
1201 	 * destroy any wsi that are associated with us but have no socket
1202 	 * (and will otherwise be missed for destruction)
1203 	 */
1204 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1205 			      vh->vh_awaiting_socket_owner.head) {
1206 		struct lws *w =
1207 			lws_container_of(d, struct lws, vh_awaiting_socket);
1208 
1209 		if (w->tsi == tsi) {
1210 
1211 			lwsl_vhost_debug(vh, "closing aso");
1212 			lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,
1213 					   "awaiting skt");
1214 		}
1215 
1216 	} lws_end_foreach_dll_safe(d, d1);
1217 #endif
1218 
1219 	/*
1220 	 * Close any wsi on this pt bound to the vhost
1221 	 */
1222 
1223 	n = 0;
1224 	while (n < pt->fds_count) {
1225 		struct lws *wsi = wsi_from_fd(ctx, pt->fds[n].fd);
1226 
1227 		if (wsi && wsi->tsi == tsi && wsi->a.vhost == vh) {
1228 
1229 			lwsl_wsi_debug(wsi, "pt %d: closin, role %s", tsi,
1230 					    wsi->role_ops->name);
1231 
1232 			lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
1233 
1234 			if (pt->pipe_wsi == wsi)
1235 				pt->pipe_wsi = NULL;
1236 		}
1237 		n++;
1238 	}
1239 
1240 #if LWS_MAX_SMP > 1
1241 	/* calling pt thread has done its wsi dieback */
1242 	vh->close_flow_vs_tsi[lws_pthread_self_to_tsi(vh->context)] = 1;
1243 #endif
1244 }
1245 
1246 #if defined(LWS_WITH_NETWORK)
1247 
1248 /* returns nonzero if v1 and v2 can share listen sockets */
1249 int
1250 lws_vhost_compare_listen(struct lws_vhost *v1, struct lws_vhost *v2)
1251 {
1252 	return ((!v1->iface && !v2->iface) ||
1253 		 (v1->iface && v2->iface && !strcmp(v1->iface, v2->iface))) &&
1254 		v1->listen_port == v2->listen_port;
1255 }
1256 
1257 /* helper to interate every listen socket on any vhost and call cb on it */
1258 int
1259 lws_vhost_foreach_listen_wsi(struct lws_context *cx, void *arg,
1260 			     lws_dll2_foreach_cb_t cb)
1261 {
1262 	struct lws_vhost *v = cx->vhost_list;
1263 	int n;
1264 
1265 	while (v) {
1266 
1267 		n = lws_dll2_foreach_safe(&v->listen_wsi, arg, cb);
1268 		if (n)
1269 			return n;
1270 
1271 		v = v->vhost_next;
1272 	}
1273 
1274 	return 0;
1275 }
1276 
1277 #endif
1278 
1279 /*
1280  * Mark the vhost as being destroyed, so things trying to use it abort.
1281  *
1282  * Dispose of the listen socket.
1283  */
1284 
1285 void
1286 lws_vhost_destroy1(struct lws_vhost *vh)
1287 {
1288 	struct lws_context *context = vh->context;
1289 	int n;
1290 
1291 	lwsl_vhost_info(vh, "\n");
1292 
1293 	lws_context_lock(context, "vhost destroy 1"); /* ---------- context { */
1294 
1295 	if (vh->being_destroyed)
1296 		goto out;
1297 
1298 	/*
1299 	 * let's lock all the pts, to enforce pt->vh order... pt is refcounted
1300 	 * so it's OK if we acquire it later inside this
1301 	 */
1302 
1303 	for (n = 0; n < context->count_threads; n++)
1304 		lws_pt_lock((&context->pt[n]), __func__);
1305 
1306 	lws_vhost_lock(vh); /* -------------- vh { */
1307 
1308 #if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_WITH_TLS)
1309 	lws_tls_session_vh_destroy(vh);
1310 #endif
1311 
1312 	vh->being_destroyed = 1;
1313 	lws_dll2_add_tail(&vh->vh_being_destroyed_list,
1314 			  &context->owner_vh_being_destroyed);
1315 
1316 #if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_SERVER)
1317 	/*
1318 	 * PHASE 1: take down or reassign any listen wsi
1319 	 *
1320 	 * Are there other vhosts that are piggybacking on our listen sockets?
1321 	 * If so we need to hand each listen socket off to one of the others
1322 	 * so it will remain open.
1323 	 *
1324 	 * If not, close the listen socket now.
1325 	 *
1326 	 * Either way the listen socket response to the vhost close is
1327 	 * immediately performed.
1328 	 */
1329 
1330 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1331 			      lws_dll2_get_head(&vh->listen_wsi)) {
1332 		struct lws *wsi = lws_container_of(d, struct lws, listen_list);
1333 
1334 		/*
1335 		 * For each of our listen sockets, check every other vhost to
1336 		 * see if another vhost should be given our listen socket.
1337 		 *
1338 		 * ipv4 and ipv6 sockets will both match and be migrated.
1339 		 */
1340 
1341 		lws_start_foreach_ll(struct lws_vhost *, v,
1342 				     context->vhost_list) {
1343 			if (v != vh && !v->being_destroyed &&
1344 			    lws_vhost_compare_listen(v, vh)) {
1345 				/*
1346 				 * this can only be a listen wsi, which is
1347 				 * restricted... it has no protocol or other
1348 				 * bindings or states.  So we can simply
1349 				 * swap it to a vhost that has the same
1350 				 * iface + port, but is not closing.
1351 				 */
1352 
1353 				lwsl_vhost_notice(vh, "listen skt migrate -> %s",
1354 						      lws_vh_tag(v));
1355 
1356 				lws_dll2_remove(&wsi->listen_list);
1357 				lws_dll2_add_tail(&wsi->listen_list,
1358 						  &v->listen_wsi);
1359 
1360 				/* req cx + vh lock */
1361 				/*
1362 				 * If the vhost sees it's being destroyed and
1363 				 * in the unbind the number of wsis bound to
1364 				 * it falls to zero, it will destroy the
1365 				 * vhost opportunistically before we can
1366 				 * complete the transfer.  Add a fake wsi
1367 				 * bind temporarily to disallow this...
1368 				 */
1369 				v->count_bound_wsi++;
1370 				__lws_vhost_unbind_wsi(wsi);
1371 				lws_vhost_bind_wsi(v, wsi);
1372 				/*
1373 				 * ... remove the fake wsi bind
1374 				 */
1375 				v->count_bound_wsi--;
1376 				break;
1377 			}
1378 		} lws_end_foreach_ll(v, vhost_next);
1379 
1380 	} lws_end_foreach_dll_safe(d, d1);
1381 
1382 	/*
1383 	 * If any listen wsi left we couldn't pass to other vhosts, close them
1384 	 */
1385 
1386 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1387 			           lws_dll2_get_head(&vh->listen_wsi)) {
1388 		struct lws *wsi = lws_container_of(d, struct lws, listen_list);
1389 
1390 		lws_dll2_remove(&wsi->listen_list);
1391 		lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
1392 
1393 	} lws_end_foreach_dll_safe(d, d1);
1394 
1395 #endif
1396 #if defined(LWS_WITH_TLS_JIT_TRUST)
1397 	lws_sul_cancel(&vh->sul_unref);
1398 #endif
1399 
1400 	lws_vhost_unlock(vh); /* } vh -------------- */
1401 
1402 	for (n = 0; n < context->count_threads; n++)
1403 		lws_pt_unlock((&context->pt[n]));
1404 
1405 out:
1406 	lws_context_unlock(context); /* --------------------------- context { */
1407 }
1408 
1409 #if defined(LWS_WITH_ABSTRACT)
1410 static int
1411 destroy_ais(struct lws_dll2 *d, void *user)
1412 {
1413 	lws_abs_t *ai = lws_container_of(d, lws_abs_t, abstract_instances);
1414 
1415 	lws_abs_destroy_instance(&ai);
1416 
1417 	return 0;
1418 }
1419 #endif
1420 
1421 /*
1422  * Either start close or destroy any wsi on the vhost that belong to this pt,
1423  * if SMP mark the vh that we have done it for
1424  *
1425  * Must not have lock on vh
1426  */
1427 
1428 void
1429 __lws_vhost_destroy2(struct lws_vhost *vh)
1430 {
1431 	const struct lws_protocols *protocol = NULL;
1432 	struct lws_context *context = vh->context;
1433 	struct lws wsi;
1434 	int n;
1435 
1436 	vh->being_destroyed = 0;
1437 
1438 	// lwsl_info("%s: %s\n", __func__, vh->name);
1439 
1440 	/*
1441 	 * let the protocols destroy the per-vhost protocol objects
1442 	 */
1443 
1444 	memset(&wsi, 0, sizeof(wsi));
1445 	wsi.a.context = vh->context;
1446 	wsi.a.vhost = vh; /* not a real bound wsi */
1447 	protocol = vh->protocols;
1448 	if (protocol && vh->created_vhost_protocols) {
1449 		n = 0;
1450 		while (n < vh->count_protocols) {
1451 			wsi.a.protocol = protocol;
1452 
1453 			lwsl_vhost_debug(vh, "protocol destroy");
1454 
1455 			if (protocol->callback)
1456 				protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
1457 					   NULL, NULL, 0);
1458 			protocol++;
1459 			n++;
1460 		}
1461 	}
1462 
1463 	/*
1464 	 * remove vhost from context list of vhosts
1465 	 */
1466 
1467 	lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) {
1468 		if (*pv == vh) {
1469 			*pv = vh->vhost_next;
1470 			break;
1471 		}
1472 	} lws_end_foreach_llp(pv, vhost_next);
1473 
1474 	/* add ourselves to the pending destruction list */
1475 
1476 	if (vh->context->vhost_pending_destruction_list != vh) {
1477 		vh->vhost_next = vh->context->vhost_pending_destruction_list;
1478 		vh->context->vhost_pending_destruction_list = vh;
1479 	}
1480 
1481 	//lwsl_debug("%s: do dfl '%s'\n", __func__, vh->name);
1482 
1483 	/* remove ourselves from the pending destruction list */
1484 
1485 	lws_start_foreach_llp(struct lws_vhost **, pv,
1486 			      context->vhost_pending_destruction_list) {
1487 		if ((*pv) == vh) {
1488 			*pv = (*pv)->vhost_next;
1489 			break;
1490 		}
1491 	} lws_end_foreach_llp(pv, vhost_next);
1492 
1493 	/*
1494 	 * Free all the allocations associated with the vhost
1495 	 */
1496 
1497 	protocol = vh->protocols;
1498 	if (protocol) {
1499 		n = 0;
1500 		while (n < vh->count_protocols) {
1501 			if (vh->protocol_vh_privs &&
1502 			    vh->protocol_vh_privs[n]) {
1503 				lws_free(vh->protocol_vh_privs[n]);
1504 				vh->protocol_vh_privs[n] = NULL;
1505 			}
1506 			protocol++;
1507 			n++;
1508 		}
1509 	}
1510 	if (vh->protocol_vh_privs)
1511 		lws_free(vh->protocol_vh_privs);
1512 	lws_ssl_SSL_CTX_destroy(vh);
1513 	lws_free(vh->same_vh_protocol_owner);
1514 
1515 	if (
1516 #if defined(LWS_WITH_PLUGINS)
1517 		context->plugin_list ||
1518 #endif
1519 	    (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) ||
1520 	    vh->allocated_vhost_protocols)
1521 		lws_free((void *)vh->protocols);
1522 #if defined(LWS_WITH_NETWORK)
1523 	LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
1524 	if (lws_rops_fidx(ar, LWS_ROPS_destroy_vhost))
1525 		lws_rops_func_fidx(ar, LWS_ROPS_destroy_vhost).
1526 							destroy_vhost(vh);
1527 	LWS_FOR_EVERY_AVAILABLE_ROLE_END;
1528 #endif
1529 
1530 #ifdef LWS_WITH_ACCESS_LOG
1531 	if (vh->log_fd != (int)LWS_INVALID_FILE)
1532 		close(vh->log_fd);
1533 #endif
1534 
1535 #if defined (LWS_WITH_TLS)
1536 	lws_free_set_NULL(vh->tls.alloc_cert_path);
1537 #endif
1538 
1539 #if LWS_MAX_SMP > 1
1540 	lws_mutex_refcount_destroy(&vh->mr);
1541 #endif
1542 
1543 #if defined(LWS_WITH_UNIX_SOCK)
1544 	if (LWS_UNIX_SOCK_ENABLED(vh)) {
1545 		n = unlink(vh->iface);
1546 		if (n)
1547 			lwsl_vhost_info(vh, "Closing unix socket %s: errno %d\n",
1548 				  vh->iface, errno);
1549 	}
1550 #endif
1551 	/*
1552 	 * although async event callbacks may still come for wsi handles with
1553 	 * pending close in the case of asycn event library like libuv,
1554 	 * they do not refer to the vhost.  So it's safe to free.
1555 	 */
1556 
1557 	if (vh->finalize)
1558 		vh->finalize(vh, vh->finalize_arg);
1559 
1560 #if defined(LWS_WITH_ABSTRACT)
1561 	/*
1562 	 * abstract instances
1563 	 */
1564 
1565 	lws_dll2_foreach_safe(&vh->abstract_instances_owner, NULL, destroy_ais);
1566 #endif
1567 
1568 #if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SYS_METRICS)
1569 	lws_metric_destroy(&vh->mt_traffic_rx, 0);
1570 	lws_metric_destroy(&vh->mt_traffic_tx, 0);
1571 #endif
1572 
1573 	lws_dll2_remove(&vh->vh_being_destroyed_list);
1574 
1575 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
1576 	lws_fi_destroy(&vh->fic);
1577 #endif
1578 #if defined(LWS_WITH_TLS_JIT_TRUST)
1579 	lws_sul_cancel(&vh->sul_unref);
1580 #endif
1581 
1582 	__lws_lc_untag(vh->context, &vh->lc);
1583 
1584 	memset(vh, 0, sizeof(*vh));
1585 	lws_free(vh);
1586 }
1587 
1588 /*
1589  * Starts the vhost destroy process
1590  *
1591  * Vhosts are not simple to deal with because they are an abstraction that
1592  * crosses SMP thread boundaries, a wsi on any pt can bind to any vhost.  If we
1593  * want another pt to do something to its wsis safely, we have to asynchronously
1594  * ask it to do it.
1595  *
1596  * In addition, with event libs, closing any handles (which are bound to vhosts
1597  * in their wsi) can happens asynchronously, so we can't just linearly do some
1598  * cleanup flow and free it in one step.
1599  *
1600  * The vhost destroy is cut into two pieces:
1601  *
1602  * 1) dispose of the listen socket, either by passing it on to another vhost
1603  *    that was already sharing it, or just closing it.
1604  *
1605  *    If any wsi bound to the vhost, mark the vhost as in the process of being
1606  *    destroyed, triggering each pt to close all wsi bound to the vhost next
1607  *    time around the event loop.  Call lws_cancel_service() so all the pts wake
1608  *    to deal with this without long poll waits making delays.
1609  *
1610  * 2) When the number of wsis bound to the vhost reaches zero, do the final
1611  *    vhost destroy flow, this can be triggered from any pt.
1612  */
1613 
1614 void
1615 lws_vhost_destroy(struct lws_vhost *vh)
1616 {
1617 	struct lws_context *context = vh->context;
1618 
1619 	lws_context_lock(context, __func__); /* ------ context { */
1620 
1621 	/* dispose of the listen socket one way or another */
1622 	lws_vhost_destroy1(vh);
1623 
1624 	/* start async closure of all wsi on this pt thread attached to vh */
1625 	__lws_vhost_destroy_pt_wsi_dieback_start(vh);
1626 
1627 	lwsl_vhost_info(vh, "count_bound_wsi %d", vh->count_bound_wsi);
1628 
1629 	/* if there are none, finalize now since no further chance */
1630 	if (!vh->count_bound_wsi) {
1631 		__lws_vhost_destroy2(vh);
1632 
1633 		goto out;
1634 	}
1635 
1636 	/*
1637 	 * We have some wsi bound to this vhost, we have to wait for these to
1638 	 * complete close and unbind before progressing the vhost removal.
1639 	 *
1640 	 * When the last bound wsi on this vh is destroyed we will auto-call
1641 	 * __lws_vhost_destroy2() to finalize vh destruction
1642 	 */
1643 
1644 #if LWS_MAX_SMP > 1
1645 	/* alert other pts they also need to do dieback flow for their wsi */
1646 	lws_cancel_service(context);
1647 #endif
1648 
1649 out:
1650 	lws_context_unlock(context); /* } context ------------------- */
1651 }
1652 
1653 
1654 void *
1655 lws_vhost_user(struct lws_vhost *vhost)
1656 {
1657 	return vhost->user;
1658 }
1659 
1660 int
1661 lws_get_vhost_listen_port(struct lws_vhost *vhost)
1662 {
1663 	return vhost->listen_port;
1664 }
1665 
1666 #if defined(LWS_WITH_SERVER)
1667 void
1668 lws_context_deprecate(struct lws_context *cx, lws_reload_func cb)
1669 {
1670 	struct lws_vhost *vh = cx->vhost_list;
1671 
1672 	/*
1673 	 * "deprecation" means disable the cx from accepting any new
1674 	 * connections and free up listen sockets to be used by a replacement
1675 	 * cx.
1676 	 *
1677 	 * Otherwise the deprecated cx remains operational, until its
1678 	 * number of connected sockets falls to zero, when it is deleted.
1679 	 *
1680 	 * So, for each vhost, close his listen sockets
1681 	 */
1682 
1683 	while (vh) {
1684 
1685 		lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1686 					   lws_dll2_get_head(&vh->listen_wsi)) {
1687 			struct lws *wsi = lws_container_of(d, struct lws,
1688 							   listen_list);
1689 
1690 			wsi->socket_is_permanently_unusable = 1;
1691 			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
1692 					   __func__);
1693 			cx->deprecation_pending_listen_close_count++;
1694 
1695 		} lws_end_foreach_dll_safe(d, d1);
1696 
1697 		vh = vh->vhost_next;
1698 	}
1699 
1700 	cx->deprecated = 1;
1701 	cx->deprecation_cb = cb;
1702 }
1703 #endif
1704 
1705 #if defined(LWS_WITH_NETWORK)
1706 
1707 struct lws_vhost *
1708 lws_get_vhost_by_name(struct lws_context *context, const char *name)
1709 {
1710 	lws_start_foreach_ll(struct lws_vhost *, v,
1711 			     context->vhost_list) {
1712 		if (!v->being_destroyed && !strcmp(v->name, name))
1713 			return v;
1714 
1715 	} lws_end_foreach_ll(v, vhost_next);
1716 
1717 	return NULL;
1718 }
1719 
1720 
1721 #if defined(LWS_WITH_CLIENT)
1722 /*
1723  * This is the logic checking to see if the new connection wsi should have a
1724  * pipelining or muxing relationship with an existing "active connection" to
1725  * the same endpoint under the same conditions.
1726  *
1727  * This was originally in the client code but since the list is held on the
1728  * vhost (to ensure the same client tls ctx is involved) it's cleaner in vhost.c
1729  *
1730  * ACTIVE_CONNS_QUEUED: We're queued on an active connection, set *nwsi to that
1731  * ACTIVE_CONNS_MUXED: We are joining an active mux conn *nwsi as a child
1732  * ACTIVE_CONNS_SOLO: There's no existing conn to join either way
1733  */
1734 
1735 int
1736 lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin)
1737 {
1738 #if defined(LWS_WITH_TLS)
1739 	const char *my_alpn = lws_wsi_client_stash_item(wsi, CIS_ALPN,
1740 							_WSI_TOKEN_CLIENT_ALPN);
1741 #endif
1742 #if defined(LWS_WITH_TLS)
1743 	char newconn_cannot_use_h1 = 0;
1744 
1745 	if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) &&
1746 	    my_alpn && !strstr(my_alpn, "http/1.1"))
1747 		/*
1748 		 * new guy wants to use tls, he specifies the alpn and he does
1749 		 * not list h1 as a choice ==> he can't bind to existing h1
1750 		 */
1751 		newconn_cannot_use_h1 = 1;
1752 #endif
1753 
1754 	if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) {
1755 		struct lws *w = lws_container_of(
1756 				wsi->dll2_cli_txn_queue.owner, struct lws,
1757 				dll2_cli_txn_queue_owner);
1758 		*nwsi = w;
1759 
1760 		return ACTIVE_CONNS_QUEUED;
1761 	}
1762 
1763 #if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT)
1764 	if (wsi->mux.parent_wsi) {
1765 		/*
1766 		 * We already decided...
1767 		 */
1768 
1769 		*nwsi = wsi->mux.parent_wsi;
1770 
1771 		return ACTIVE_CONNS_MUXED;
1772 	}
1773 #endif
1774 
1775 	lws_context_lock(wsi->a.context, __func__); /* -------------- cx { */
1776 	lws_vhost_lock(wsi->a.vhost); /* ----------------------------------- { */
1777 
1778 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1779 				   wsi->a.vhost->dll_cli_active_conns_owner.head) {
1780 		struct lws *w = lws_container_of(d, struct lws,
1781 						 dll_cli_active_conns);
1782 
1783 		lwsl_wsi_debug(wsi, "check %s %s %s %d %d",
1784 				    lws_wsi_tag(w), adsin,
1785 				    w->cli_hostname_copy ? w->cli_hostname_copy :
1786 							    "null",
1787 				    wsi->c_port, w->c_port);
1788 
1789 		if (w != wsi &&
1790 		    /*
1791 		     * "same internet protocol"... this is a bit tricky,
1792 		     * since h2 start out as h1, and may stay at h1.
1793 		     *
1794 		     * But an idle h1 connection cannot be used by a connection
1795 		     * request that doesn't have http/1.1 in its alpn list...
1796 		     */
1797 		    (w->role_ops == wsi->role_ops ||
1798 		     (lwsi_role_http(w) && lwsi_role_http(wsi))) &&
1799 		     /* ... same role, or at least both some kind of http */
1800 		    w->cli_hostname_copy && !strcmp(adsin, w->cli_hostname_copy) &&
1801 		    /* same endpoint hostname */
1802 #if defined(LWS_WITH_TLS)
1803 		   !(newconn_cannot_use_h1 && w->role_ops == &role_ops_h1) &&
1804 		   /* if we can't use h1, old guy must not be h1 */
1805 		    (wsi->tls.use_ssl & LCCSCF_USE_SSL) ==
1806 		     (w->tls.use_ssl & LCCSCF_USE_SSL) &&
1807 		     /* must both agree on tls use or not */
1808 #endif
1809 		    wsi->c_port == w->c_port) {
1810 			/* same endpoint port */
1811 
1812 			/*
1813 			 * There's already an active connection.
1814 			 *
1815 			 * The server may have told the existing active
1816 			 * connection that it doesn't support pipelining...
1817 			 */
1818 			if (w->keepalive_rejected) {
1819 				lwsl_wsi_notice(w, "defeating pipelining");
1820 				goto solo;
1821 			}
1822 
1823 #if defined(LWS_WITH_HTTP2)
1824 			/*
1825 			 * h2: if in usable state already: just use it without
1826 			 *     going through the queue
1827 			 */
1828 			if (w->client_h2_alpn && w->client_mux_migrated &&
1829 			    (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS ||
1830 			     lwsi_state(w) == LRS_ESTABLISHED ||
1831 			     lwsi_state(w) == LRS_IDLING)) {
1832 
1833 				lwsl_wsi_notice(w, "just join h2 directly 0x%x",
1834 						   lwsi_state(w));
1835 
1836 				if (lwsi_state(w) == LRS_IDLING)
1837 					_lws_generic_transaction_completed_active_conn(&w, 0);
1838 
1839 				//lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2);
1840 
1841 				wsi->client_h2_alpn = 1;
1842 				lws_wsi_h2_adopt(w, wsi);
1843 				lws_vhost_unlock(wsi->a.vhost); /* } ---------- */
1844 				lws_context_unlock(wsi->a.context); /* -------------- cx { */
1845 
1846 				*nwsi = w;
1847 
1848 				return ACTIVE_CONNS_MUXED;
1849 			}
1850 #endif
1851 
1852 #if defined(LWS_ROLE_MQTT)
1853 			/*
1854 			 * MQTT: if in usable state already: just use it without
1855 			 *	 going through the queue
1856 			 */
1857 
1858 			if (lwsi_role_mqtt(wsi) && w->client_mux_migrated &&
1859 			    lwsi_state(w) == LRS_ESTABLISHED) {
1860 
1861 				if (lws_wsi_mqtt_adopt(w, wsi)) {
1862 					lwsl_wsi_notice(w, "join mqtt directly");
1863 					lws_dll2_remove(&wsi->dll2_cli_txn_queue);
1864 					wsi->client_mux_substream = 1;
1865 
1866 					lws_vhost_unlock(wsi->a.vhost); /* } ---------- */
1867 					lws_context_unlock(wsi->a.context); /* -------------- cx { */
1868 
1869 					return ACTIVE_CONNS_MUXED;
1870 				}
1871 			}
1872 #endif
1873 
1874 			/*
1875 			 * If the connection is viable but not yet in a usable
1876 			 * state, let's attach ourselves to it and wait for it
1877 			 * to get there or fail.
1878 			 */
1879 
1880 			lwsl_wsi_notice(wsi, "apply txn queue %s, state 0x%lx",
1881 					     lws_wsi_tag(w),
1882 					     (unsigned long)w->wsistate);
1883 			/*
1884 			 * ...let's add ourselves to his transaction queue...
1885 			 * we are adding ourselves at the TAIL
1886 			 */
1887 			lws_dll2_add_tail(&wsi->dll2_cli_txn_queue,
1888 					  &w->dll2_cli_txn_queue_owner);
1889 
1890 			if (lwsi_state(w) == LRS_IDLING)
1891 				_lws_generic_transaction_completed_active_conn(&w, 0);
1892 
1893 			/*
1894 			 * For eg, h1 next we'd pipeline our headers out on him,
1895 			 * and wait for our turn at client transaction_complete
1896 			 * to take over parsing the rx.
1897 			 */
1898 			lws_vhost_unlock(wsi->a.vhost); /* } ---------- */
1899 			lws_context_unlock(wsi->a.context); /* -------------- cx { */
1900 
1901 			*nwsi = w;
1902 
1903 			return ACTIVE_CONNS_QUEUED;
1904 		}
1905 
1906 	} lws_end_foreach_dll_safe(d, d1);
1907 
1908 solo:
1909 	lws_vhost_unlock(wsi->a.vhost); /* } ---------------------------------- */
1910 	lws_context_unlock(wsi->a.context); /* -------------- cx { */
1911 
1912 	/* there is nobody already connected in the same way */
1913 
1914 	return ACTIVE_CONNS_SOLO;
1915 }
1916 #endif
1917 #endif
1918 
1919 const char *
1920 lws_vh_tag(struct lws_vhost *vh)
1921 {
1922 	return lws_lc_tag(&vh->lc);
1923 }
1924 
1925 struct lws_log_cx *
1926 lwsl_vhost_get_cx(struct lws_vhost *vh)
1927 {
1928 	if (!vh)
1929 		return NULL;
1930 
1931 	return vh->lc.log_cx;
1932 }
1933 
1934 void
1935 lws_log_prepend_vhost(struct lws_log_cx *cx, void *obj, char **p, char *e)
1936 {
1937 	struct lws_vhost *vh = (struct lws_vhost *)obj;
1938 
1939 	*p += lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), "%s: ",
1940 							lws_vh_tag(vh));
1941 }
1942