• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2019 - 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  * When the user code is in a different process, a non-tls unix domain socket
26  * proxy is used to asynchronusly transfer buffers in each direction via the
27  * network stack, without explicit IPC
28  *
29  *     user_process{ [user code] | shim | socket-}------ lws_process{ lws }
30  *
31  * Lws exposes a listening unix domain socket in this case, the user processes
32  * connect to it and pass just info.streamtype in an initial tx packet.  All
33  * packets are prepended by a 1-byte type field when used in this mode.  See
34  * lws-secure-streams.h for documentation and definitions.
35  *
36  * Proxying in either direction can face the situation it cannot send the onward
37  * packet immediately and is subject to separating the write request from the
38  * write action.  To make the best use of memory, a single preallocated buffer
39  * stashes pending packets in all four directions (c->p, p->c, p->ss, ss->p).
40  * This allows it to adapt to different traffic patterns without wasted areas
41  * dedicated to traffic that isn't coming in a particular application.
42  *
43  * A shim is provided to monitor the process' unix domain socket and regenerate
44  * the secure sockets api there with callbacks happening in the process thread
45  * context.
46  *
47  * This file implements the listening unix domain socket proxy... this code is
48  * only going to run on a Linux-class device with its implications about memory
49  * availability.
50  */
51 
52 #include <private-lib-core.h>
53 
54 struct raw_pss {
55 	struct conn		*conn;
56 };
57 
58 /*
59  * Proxy - onward secure-stream handler
60  */
61 
62 typedef struct ss_proxy_onward {
63 	lws_ss_handle_t 	*ss;
64 	struct conn		*conn;
65 } ss_proxy_t;
66 
67 void
lws_proxy_clean_conn_ss(struct lws * wsi)68 lws_proxy_clean_conn_ss(struct lws *wsi)
69 {
70 #if 0
71 	lws_ss_handle_t *h = (lws_ss_handle_t *)wsi->a.opaque_user_data;
72 	struct conn *conn = h->conn_if_sspc_onw;
73 
74 	if (!wsi)
75 		return;
76 
77 	if (conn && conn->ss)
78 		conn->ss->wsi = NULL;
79 #endif
80 }
81 
82 
83 void
ss_proxy_onward_link_req_writeable(lws_ss_handle_t * h_onward)84 ss_proxy_onward_link_req_writeable(lws_ss_handle_t *h_onward)
85 {
86 	ss_proxy_t *m = (ss_proxy_t *)&h_onward[1];
87 
88 	if (m->conn->wsi) /* if possible, request client conn write */
89 		lws_callback_on_writable(m->conn->wsi);
90 }
91 
92 int
__lws_ss_proxy_bind_ss_to_conn_wsi(void * parconn,size_t dsh_size)93 __lws_ss_proxy_bind_ss_to_conn_wsi(void *parconn, size_t dsh_size)
94 {
95 	struct conn *conn = (struct conn *)parconn;
96 	struct lws_context_per_thread *pt;
97 
98 	if (!conn || !conn->wsi || !conn->ss)
99 		return -1;
100 
101 	pt = &conn->wsi->a.context->pt[(int)conn->wsi->tsi];
102 
103 	if (lws_fi(&conn->ss->fic, "ssproxy_dsh_create_oom"))
104 		return -1;
105 	conn->dsh = lws_dsh_create(&pt->ss_dsh_owner, dsh_size, 2);
106 	if (!conn->dsh)
107 		return -1;
108 
109 	__lws_lc_tag_append(&conn->wsi->lc, lws_ss_tag(conn->ss));
110 
111 	return 0;
112 }
113 
114 /* Onward secure streams payload interface */
115 
116 static lws_ss_state_return_t
ss_proxy_onward_rx(void * userobj,const uint8_t * buf,size_t len,int flags)117 ss_proxy_onward_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
118 {
119 	ss_proxy_t *m = (ss_proxy_t *)userobj;
120 	const char *rsp = NULL;
121 	int n;
122 
123 	// lwsl_notice("%s: len %d\n", __func__, (int)len);
124 
125 	/*
126 	 * The onward secure stream connection has received something.
127 	 */
128 
129 	if (m->ss->rideshare != m->ss->policy && m->ss->rideshare) {
130 		rsp = m->ss->rideshare->streamtype;
131 		flags |= LWSSS_FLAG_RIDESHARE;
132 	}
133 
134 	/*
135 	 * Apply SSS framing around this chunk of RX and stash it in the dsh
136 	 * in ss -> proxy [ -> client] direction.  This can fail...
137 	 */
138 
139 	if (lws_fi(&m->ss->fic, "ssproxy_dsh_rx_queue_oom"))
140 		n = 1;
141 	else
142 		n = lws_ss_serialize_rx_payload(m->conn->dsh, buf, len,
143 						flags, rsp);
144 	if (n)
145 		/*
146 		 * We couldn't buffer this rx, eg due to OOM, let's escalate it
147 		 * to be a "loss of connection", which it basically is...
148 		 */
149 		return LWSSSSRET_DISCONNECT_ME;
150 
151 	/*
152 	 * Manage rx flow on the SS (onward) side according to our situation
153 	 * in the dsh holding proxy->client serialized forwarding rx
154 	 */
155 
156 	if (!m->conn->onward_in_flow_control && m->ss->wsi &&
157 	    m->ss->policy->proxy_buflen_rxflow_on_above &&
158 	    lws_dsh_get_size(m->conn->dsh, KIND_SS_TO_P) >=
159 				m->ss->policy->proxy_buflen_rxflow_on_above) {
160 		lwsl_info("%s: %s: rxflow disabling rx (%lu / %lu, hwm %lu)\n", __func__,
161 				lws_wsi_tag(m->ss->wsi),
162 				(unsigned long)lws_dsh_get_size(m->conn->dsh, KIND_SS_TO_P),
163 				(unsigned long)m->ss->policy->proxy_buflen,
164 				(unsigned long)m->ss->policy->proxy_buflen_rxflow_on_above);
165 		/*
166 		 * stop taking in rx once the onward wsi rx is above the
167 		 * high water mark
168 		 */
169 		lws_rx_flow_control(m->ss->wsi, 0);
170 		m->conn->onward_in_flow_control = 1;
171 	}
172 
173 	if (m->conn->wsi) /* if possible, request client conn write */
174 		lws_callback_on_writable(m->conn->wsi);
175 
176 	return LWSSSSRET_OK;
177 }
178 
179 /*
180  * we are transmitting buffered payload originally from the client on to the ss
181  */
182 
183 static lws_ss_state_return_t
ss_proxy_onward_tx(void * userobj,lws_ss_tx_ordinal_t ord,uint8_t * buf,size_t * len,int * flags)184 ss_proxy_onward_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
185 		   size_t *len, int *flags)
186 {
187 	ss_proxy_t *m = (ss_proxy_t *)userobj;
188 	void *p;
189 	size_t si;
190 
191 	if (!m->conn->ss || m->conn->state != LPCSPROX_OPERATIONAL) {
192 		lwsl_notice("%s: ss not ready\n", __func__);
193 		*len = 0;
194 
195 		return LWSSSSRET_TX_DONT_SEND;
196 	}
197 
198 	/*
199 	 * The onward secure stream says that we could send something to it
200 	 * (by putting it in buf, and setting *len and *flags)... dredge the
201 	 * next thing out of the dsh
202 	 */
203 
204 	if (lws_ss_deserialize_tx_payload(m->conn->dsh, m->ss->wsi,
205 					  ord, buf, len, flags))
206 		return LWSSSSRET_TX_DONT_SEND;
207 
208 	/* ... there's more we want to send? */
209 	if (!lws_dsh_get_head(m->conn->dsh, KIND_C_TO_P, (void **)&p, &si))
210 		_lws_ss_request_tx(m->conn->ss);
211 
212 	if (!*len && !*flags)
213 		/* we don't actually want to send anything */
214 		return LWSSSSRET_TX_DONT_SEND;
215 
216 	lwsl_info("%s: onward tx %d fl 0x%x\n", __func__, (int)*len, *flags);
217 
218 #if 0
219 	{
220 		int ff = open("/tmp/z", O_RDWR | O_CREAT | O_APPEND, 0666);
221 		if (ff == -1)
222 			lwsl_err("%s: errno %d\n", __func__, errno);
223 		write(ff, buf, *len);
224 		close(ff);
225 	}
226 #endif
227 
228 	return LWSSSSRET_OK;
229 }
230 
231 static lws_ss_state_return_t
ss_proxy_onward_state(void * userobj,void * sh,lws_ss_constate_t state,lws_ss_tx_ordinal_t ack)232 ss_proxy_onward_state(void *userobj, void *sh,
233 		      lws_ss_constate_t state, lws_ss_tx_ordinal_t ack)
234 {
235 	ss_proxy_t *m = (ss_proxy_t *)userobj;
236 	size_t dsh_size;
237 
238 	switch (state) {
239 	case LWSSSCS_CREATING:
240 
241 		/*
242 		 * conn is private to -process.c, call thru to a) adjust
243 		 * the accepted incoming proxy link wsi tag name to be
244 		 * appended with the onward ss tag information now we
245 		 * have it, and b) allocate the dsh buffer now we
246 		 * can find out the policy about it for the streamtype.
247 		 */
248 
249 		dsh_size = m->ss->policy->proxy_buflen ?
250 				m->ss->policy->proxy_buflen : 32768;
251 
252 		lwsl_notice("%s: %s: initializing dsh max len %lu\n",
253 				__func__, lws_ss_tag(m->ss),
254 				(unsigned long)dsh_size);
255 
256 		/* this includes ssproxy_dsh_create_oom fault generation */
257 
258 		if (__lws_ss_proxy_bind_ss_to_conn_wsi(m->conn, dsh_size)) {
259 
260 			/* failed to allocate the dsh */
261 
262 			lwsl_notice("%s: dsh init failed\n", __func__);
263 
264 			return LWSSSSRET_DESTROY_ME;
265 		}
266 		break;
267 
268 	case LWSSSCS_DESTROYING:
269 		if (!m->conn)
270 			break;
271 		if (!m->conn->wsi) {
272 			/*
273 			 * Our onward secure stream is closing and our client
274 			 * connection has already gone away... destroy the conn.
275 			 */
276 			lwsl_info("%s: Destroying conn\n", __func__);
277 			lws_dsh_destroy(&m->conn->dsh);
278 			free(m->conn);
279 			m->conn = NULL;
280 			return 0;
281 		} else
282 			lwsl_info("%s: ss DESTROYING, wsi up\n", __func__);
283 		break;
284 
285 	default:
286 		break;
287 	}
288 	if (!m->conn) {
289 		lwsl_warn("%s: dropping state due to conn not up\n", __func__);
290 
291 		return LWSSSSRET_OK;
292 	}
293 
294 	if (lws_ss_serialize_state(m->conn->wsi, m->conn->dsh, state, ack))
295 		/*
296 		 * Failed to alloc state packet that we want to send in dsh,
297 		 * we will lose coherence and have to disconnect the link
298 		 */
299 		return LWSSSSRET_DISCONNECT_ME;
300 
301 	if (m->conn->wsi) /* if possible, request client conn write */
302 		lws_callback_on_writable(m->conn->wsi);
303 
304 	return LWSSSSRET_OK;
305 }
306 
307 void
ss_proxy_onward_txcr(void * userobj,int bump)308 ss_proxy_onward_txcr(void *userobj, int bump)
309 {
310 	ss_proxy_t *m = (ss_proxy_t *)userobj;
311 
312 	if (!m->conn)
313 		return;
314 
315 	lws_ss_serialize_txcr(m->conn->dsh, bump);
316 
317 	if (m->conn->wsi) /* if possible, request client conn write */
318 		lws_callback_on_writable(m->conn->wsi);
319 }
320 
321 /*
322  * Client <-> Proxy connection, usually on Unix Domain Socket
323  */
324 
325 static int
callback_ss_proxy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)326 callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
327 		  void *user, void *in, size_t len)
328 {
329 	struct raw_pss *pss = (struct raw_pss *)user;
330 	const lws_ss_policy_t *rsp;
331 	struct conn *conn = NULL;
332 	lws_ss_metadata_t *md;
333 	lws_ss_info_t ssi;
334 	const uint8_t *cp;
335 	char s[512];
336 	uint8_t *p;
337 	size_t si;
338 	char pay;
339 	int n;
340 
341 	if (pss)
342 		conn = pss->conn;
343 
344 	switch (reason) {
345 	case LWS_CALLBACK_PROTOCOL_INIT:
346 		break;
347 
348 	case LWS_CALLBACK_PROTOCOL_DESTROY:
349 		break;
350 
351 	/* callbacks related to raw socket descriptor "accepted side" */
352 
353         case LWS_CALLBACK_RAW_ADOPT:
354 		lwsl_info("LWS_CALLBACK_RAW_ADOPT\n");
355 		if (!pss)
356 			return -1;
357 
358 		if (lws_fi(&wsi->fic, "ssproxy_client_adopt_oom"))
359 			pss->conn = NULL;
360 		else
361 			pss->conn = malloc(sizeof(struct conn));
362 		if (!pss->conn)
363 			return -1;
364 
365 		memset(pss->conn, 0, sizeof(*pss->conn));
366 
367 		/* dsh is allocated when the onward ss is done */
368 
369 		pss->conn->wsi = wsi;
370 		wsi->bound_ss_proxy_conn = 1; /* opaque is conn */
371 
372 		pss->conn->state = LPCSPROX_WAIT_INITIAL_TX;
373 
374 		/*
375 		 * Client is expected to follow the unix domain socket
376 		 * acceptance up rapidly with an initial tx containing the
377 		 * streamtype name.  We can't create the stream until then.
378 		 */
379 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);
380                 break;
381 
382 	case LWS_CALLBACK_RAW_CLOSE:
383 		lwsl_info("LWS_CALLBACK_RAW_CLOSE:\n");
384 
385 		if (!conn)
386 			break;
387 
388 		/*
389 		 * the client unix domain socket connection (wsi / conn->wsi)
390 		 * has closed... eg, client has exited or otherwise has
391 		 * definitively finished with the proxying and onward connection
392 		 *
393 		 * But right now, the SS and possibly the SS onward wsi are
394 		 * still live...
395 		 */
396 
397 		assert(conn->wsi == wsi);
398 		conn->wsi = NULL;
399 
400 		lwsl_notice("%s: cli->prox link %s closing\n", __func__,
401 				lws_wsi_tag(wsi));
402 
403 		/* sever relationship with conn */
404 		lws_set_opaque_user_data(wsi, NULL);
405 
406 		/*
407 		 * The current wsi is decoupled from the pss / conn and
408 		 * the conn no longer has a pointer on it.
409 		 *
410 		 * If there's an outgoing, proxied SS conn on our behalf, we
411 		 * have to destroy those
412 		 */
413 
414 		if (conn->ss) {
415 			struct lws *cw = conn->ss->wsi;
416 			/*
417 			 * conn->ss is the onward connection SS
418 			 */
419 
420 			lwsl_info("%s: destroying %s, wsi %s\n",
421 					__func__, lws_ss_tag(conn->ss),
422 					lws_wsi_tag(conn->ss->wsi));
423 
424 			/* sever conn relationship with ss about to be deleted */
425 
426 			conn->ss->wsi = NULL;
427 
428 			if (cw && wsi != cw) {
429 
430 				/* disconnect onward SS from its wsi */
431 
432 				lws_set_opaque_user_data(cw, NULL);
433 
434 				/*
435 				 * The wsi doing the onward connection can no
436 				 * longer relate to the conn... otherwise when
437 				 * he gets callbacks he wants to bind to
438 				 * the ss we are about to delete
439 				 */
440 				lws_wsi_close(cw, LWS_TO_KILL_ASYNC);
441 			}
442 
443 			lws_ss_destroy(&conn->ss);
444 			/*
445 			 * Conn may have gone, at ss destroy handler in
446 			 * ssi.state for proxied ss
447 			 */
448 			break;
449 		}
450 
451 		if (conn->state == LPCSPROX_DESTROYED || !conn->ss) {
452 			/*
453 			 * There's no onward secure stream and our client
454 			 * connection is closing.  Destroy the conn.
455 			 */
456 			lws_dsh_destroy(&conn->dsh);
457 			free(conn);
458 			pss->conn = NULL;
459 		} else
460 			lwsl_debug("%s: CLOSE; %s\n", __func__, lws_ss_tag(conn->ss));
461 
462 		break;
463 
464 	case LWS_CALLBACK_RAW_RX:
465 		/*
466 		 * ie, the proxy is receiving something from a client
467 		 */
468 		lwsl_info("%s: RX: rx %d\n", __func__, (int)len);
469 
470 		if (!conn || !conn->wsi) {
471 			lwsl_err("%s: rx with bad conn state\n", __func__);
472 
473 			return -1;
474 		}
475 
476 		// lwsl_hexdump_info(in, len);
477 
478 		if (conn->state == LPCSPROX_WAIT_INITIAL_TX) {
479 			memset(&ssi, 0, sizeof(ssi));
480 			ssi.user_alloc = sizeof(ss_proxy_t);
481 			ssi.handle_offset = offsetof(ss_proxy_t, ss);
482 			ssi.opaque_user_data_offset =
483 					offsetof(ss_proxy_t, conn);
484 			ssi.rx = ss_proxy_onward_rx;
485 			ssi.tx = ss_proxy_onward_tx;
486 		}
487 		ssi.state = ss_proxy_onward_state;
488 		ssi.flags = 0;
489 
490 		n = lws_ss_deserialize_parse(&conn->parser,
491 				lws_get_context(wsi), conn->dsh, in, len,
492 				&conn->state, conn, &conn->ss, &ssi, 0);
493 		switch (n) {
494 		case LWSSSSRET_OK:
495 			break;
496 		case LWSSSSRET_DISCONNECT_ME:
497 			return -1;
498 		case LWSSSSRET_DESTROY_ME:
499 			if (conn->ss)
500 				lws_ss_destroy(&conn->ss);
501 			return -1;
502 		}
503 
504 		if (conn->state == LPCSPROX_REPORTING_FAIL ||
505 		    conn->state == LPCSPROX_REPORTING_OK)
506 			lws_callback_on_writable(conn->wsi);
507 
508 		break;
509 
510 	case LWS_CALLBACK_RAW_WRITEABLE:
511 
512 		lwsl_debug("%s: %s: LWS_CALLBACK_RAW_WRITEABLE, state 0x%x\n",
513 				__func__, lws_wsi_tag(wsi), lwsi_state(wsi));
514 
515 		/*
516 		 * We can transmit something back to the client from the dsh
517 		 * of stuff we received on its behalf from the ss
518 		 */
519 
520 		if (!conn || !conn->wsi)
521 			break;
522 
523 		n = 0;
524 		pay = 0;
525 
526 		s[3] = 0;
527 		cp = (const uint8_t *)s;
528 		switch (conn->state) {
529 		case LPCSPROX_REPORTING_FAIL:
530 			s[3] = 1;
531 			/* fallthru */
532 		case LPCSPROX_REPORTING_OK:
533 			s[0] = LWSSS_SER_RXPRE_CREATE_RESULT;
534 			s[1] = 0;
535 			s[2] = 1;
536 
537 			n = 8;
538 
539 			lws_ser_wu32be((uint8_t *)&s[4], conn->ss &&
540 							 conn->ss->policy ?
541 					conn->ss->policy->client_buflen : 0);
542 
543 			/*
544 			 * If there's rideshare sequencing, it's added after the
545 			 * first 4 bytes or the create result, comma-separated
546 			 */
547 
548 			if (conn->ss) {
549 				rsp = conn->ss->policy;
550 
551 				while (rsp) {
552 					if (n != 4 && n < (int)sizeof(s) - 2)
553 						s[n++] = ',';
554 					n += lws_snprintf(&s[n], sizeof(s) - (unsigned int)n,
555 							"%s", rsp->streamtype);
556 					rsp = lws_ss_policy_lookup(wsi->a.context,
557 						rsp->rideshare_streamtype);
558 				}
559 			}
560 			s[2] = (char)(n - 3);
561 			conn->state = LPCSPROX_OPERATIONAL;
562 			lws_set_timeout(wsi, 0, 0);
563 			break;
564 
565 		case LPCSPROX_OPERATIONAL:
566 
567 			/*
568 			 * returning [onward -> ] proxy]-> client
569 			 * rx metadata has priority 1
570 			 */
571 
572 			md = conn->ss->metadata;
573 			while (md) {
574 				// lwsl_notice("%s: check %s: %d\n", __func__,
575 				// md->name, md->pending_onward);
576 				if (md->pending_onward) {
577 					size_t naml = strlen(md->name);
578 
579 					// lwsl_notice("%s: proxy issuing rxmd\n", __func__);
580 
581 					if (4 + naml + md->length > sizeof(s)) {
582 						lwsl_err("%s: rxmdata too big\n",
583 								__func__);
584 						goto hangup;
585 					}
586 					md->pending_onward = 0;
587 					p = (uint8_t *)s;
588 					p[0] = LWSSS_SER_RXPRE_METADATA;
589 					lws_ser_wu16be(&p[1], (uint16_t)(1 + naml +
590 							      md->length));
591 					p[3] = (uint8_t)naml;
592 					memcpy(&p[4], md->name, naml);
593 					p += 4 + naml;
594 					memcpy(p, md->value__may_own_heap,
595 					       md->length);
596 					p += md->length;
597 
598 					n = lws_ptr_diff(p, cp);
599 					goto again;
600 				}
601 
602 				md = md->next;
603 			}
604 
605 			/*
606 			 * If we have performance data, render it in JSON
607 			 * and send that in LWSSS_SER_RXPRE_PERF has
608 			 * priority 2
609 			 */
610 
611 #if defined(LWS_WITH_CONMON)
612 			if (conn->ss->conmon_json) {
613 				unsigned int xlen = conn->ss->conmon_len;
614 
615 				if (xlen > sizeof(s) - 3)
616 					xlen = sizeof(s) - 3;
617 				cp = (uint8_t *)s;
618 				p = (uint8_t *)s;
619 				p[0] = LWSSS_SER_RXPRE_PERF;
620 				lws_ser_wu16be(&p[1], (uint16_t)xlen);
621 				memcpy(&p[3], conn->ss->conmon_json, xlen);
622 
623 				lws_free_set_NULL(conn->ss->conmon_json);
624 				n = (int)(xlen + 3);
625 
626 				pay = 0;
627 				goto again;
628 			}
629 #endif
630 			/*
631 			 * if no fresh rx metadata, just pass through incoming
632 			 * dsh
633 			 */
634 
635 			if (lws_dsh_get_head(conn->dsh, KIND_SS_TO_P,
636 					     (void **)&p, &si))
637 				break;
638 
639 			cp = p;
640 
641 #if 0
642 			if (cp[0] == LWSSS_SER_RXPRE_RX_PAYLOAD &&
643 			    wsi->a.context->detailed_latency_cb) {
644 
645 				/*
646 				 * we're fulfilling rx that came in on ss
647 				 * by sending it back out to the client on
648 				 * the Unix Domain Socket
649 				 *
650 				 * +  7  u32  write will compute latency here...
651 				 * + 11  u32  ust we received from ss
652 				 *
653 				 * lws_write will report it and fill in
654 				 * LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE
655 				 */
656 
657 				us = lws_now_usecs();
658 				lws_ser_wu32be(&p[7], us -
659 						      lws_ser_ru64be(&p[11]));
660 				lws_ser_wu64be(&p[11], us);
661 
662 				wsi->detlat.acc_size =
663 					wsi->detlat.req_size = si - 19;
664 				/* time proxy held it */
665 				wsi->detlat.latencies[
666 				            LAT_DUR_PROXY_RX_TO_ONWARD_TX] =
667 							lws_ser_ru32be(&p[7]);
668 			}
669 #endif
670 			pay = 1;
671 			n = (int)si;
672 			break;
673 		default:
674 			break;
675 		}
676 again:
677 		if (!n)
678 			break;
679 
680 		if (lws_fi(&wsi->fic, "ssproxy_client_write_fail"))
681 			n = -1;
682 		else
683 			n = lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);
684 		if (n < 0) {
685 			lwsl_info("%s: WRITEABLE: %d\n", __func__, n);
686 
687 			goto hangup;
688 		}
689 
690 		switch (conn->state) {
691 		case LPCSPROX_REPORTING_FAIL:
692 			goto hangup;
693 		case LPCSPROX_OPERATIONAL:
694 			if (!conn)
695 				break;
696 			if (pay) {
697 				lws_dsh_free((void **)&p);
698 
699 				/*
700 				 * Did we go below the rx flow threshold for
701 				 * this dsh?
702 				 */
703 
704 				if (conn->onward_in_flow_control &&
705 				    conn->ss->policy->proxy_buflen_rxflow_on_above &&
706 				    conn->ss->wsi &&
707 				    lws_dsh_get_size(conn->dsh, KIND_SS_TO_P) <
708 				      conn->ss->policy->proxy_buflen_rxflow_off_below) {
709 					lwsl_info("%s: %s: rxflow enabling rx (%lu / %lu, lwm %lu)\n", __func__,
710 							lws_wsi_tag(conn->ss->wsi),
711 							(unsigned long)lws_dsh_get_size(conn->dsh, KIND_SS_TO_P),
712 							(unsigned long)conn->ss->policy->proxy_buflen,
713 							(unsigned long)conn->ss->policy->proxy_buflen_rxflow_off_below);
714 					/*
715 					 * Resume receiving taking in rx once
716 					 * below the low threshold
717 					 */
718 					lws_rx_flow_control(conn->ss->wsi,
719 							    LWS_RXFLOW_ALLOW);
720 					conn->onward_in_flow_control = 0;
721 				}
722 			}
723 			if (!lws_dsh_get_head(conn->dsh, KIND_SS_TO_P,
724 					     (void **)&p, &si)) {
725 				if (!lws_send_pipe_choked(wsi)) {
726 					cp = p;
727 					pay = 1;
728 					n = (int)si;
729 					goto again;
730 				}
731 				lws_callback_on_writable(wsi);
732 			}
733 			break;
734 		default:
735 			break;
736 		}
737 		break;
738 
739 	default:
740 		break;
741 	}
742 
743 	return lws_callback_http_dummy(wsi, reason, user, in, len);
744 
745 hangup:
746 	/* hang up on him */
747 
748 	return -1;
749 }
750 
751 static const struct lws_protocols protocols[] = {
752 	{
753 		"ssproxy-protocol",
754 		callback_ss_proxy,
755 		sizeof(struct raw_pss),
756 		2048, 2048, NULL, 0
757 	},
758 	{ NULL, NULL, 0, 0, 0, NULL, 0 }
759 };
760 
761 /*
762  * called from create_context()
763  */
764 
765 int
lws_ss_proxy_create(struct lws_context * context,const char * bind,int port)766 lws_ss_proxy_create(struct lws_context *context, const char *bind, int port)
767 {
768 	struct lws_context_creation_info info;
769 
770 	memset(&info, 0, sizeof(info));
771 
772 	info.vhost_name			= "ssproxy";
773 	info.options = LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG |
774 			LWS_SERVER_OPTION_SS_PROXY;
775 	info.port = port;
776 	if (!port) {
777 		if (!bind)
778 #if defined(__linux__)
779 			bind = "@proxy.ss.lws";
780 #else
781 			bind = "/tmp/proxy.ss.lws";
782 #endif
783 		info.options |= LWS_SERVER_OPTION_UNIX_SOCK;
784 	}
785 	info.iface			= bind;
786 #if defined(__linux__)
787 	info.unix_socket_perms		= "root:root";
788 #else
789 #endif
790 	info.listen_accept_role		= "raw-skt";
791 	info.listen_accept_protocol	= "ssproxy-protocol";
792 	info.protocols			= protocols;
793 
794 	if (!lws_create_vhost(context, &info)) {
795 		lwsl_err("%s: Failed to create ss proxy vhost\n", __func__);
796 
797 		return 1;
798 	}
799 
800 	return 0;
801 }
802