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
54struct raw_pss {
55	struct conn		*conn;
56};
57
58/*
59 * Proxy - onward secure-stream handler
60 */
61
62typedef struct ss_proxy_onward {
63	lws_ss_handle_t 	*ss;
64	struct conn		*conn;
65} ss_proxy_t;
66
67void
68lws_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
83void
84ss_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
92int
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
116static lws_ss_state_return_t
117ss_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
183static lws_ss_state_return_t
184ss_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
231static lws_ss_state_return_t
232ss_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
307void
308ss_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
325static int
326callback_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		// coverity[uninit_use_in_call]
491		n = lws_ss_deserialize_parse(&conn->parser,
492				lws_get_context(wsi), conn->dsh, in, len,
493				&conn->state, conn, &conn->ss, &ssi, 0);
494		switch (n) {
495		case LWSSSSRET_OK:
496			break;
497		case LWSSSSRET_DISCONNECT_ME:
498			return -1;
499		case LWSSSSRET_DESTROY_ME:
500			if (conn->ss)
501				lws_ss_destroy(&conn->ss);
502			return -1;
503		}
504
505		if (conn->state == LPCSPROX_REPORTING_FAIL ||
506		    conn->state == LPCSPROX_REPORTING_OK)
507			lws_callback_on_writable(conn->wsi);
508
509		break;
510
511	case LWS_CALLBACK_RAW_WRITEABLE:
512
513		lwsl_debug("%s: %s: LWS_CALLBACK_RAW_WRITEABLE, state 0x%x\n",
514				__func__, lws_wsi_tag(wsi), lwsi_state(wsi));
515
516		/*
517		 * We can transmit something back to the client from the dsh
518		 * of stuff we received on its behalf from the ss
519		 */
520
521		if (!conn || !conn->wsi)
522			break;
523
524		n = 0;
525		pay = 0;
526
527		s[3] = 0;
528		cp = (const uint8_t *)s;
529		switch (conn->state) {
530		case LPCSPROX_REPORTING_FAIL:
531			s[3] = 1;
532			/* fallthru */
533		case LPCSPROX_REPORTING_OK:
534			s[0] = LWSSS_SER_RXPRE_CREATE_RESULT;
535			s[1] = 0;
536			s[2] = 1;
537
538			n = 8;
539
540			lws_ser_wu32be((uint8_t *)&s[4], conn->ss &&
541							 conn->ss->policy ?
542					conn->ss->policy->client_buflen : 0);
543
544			/*
545			 * If there's rideshare sequencing, it's added after the
546			 * first 4 bytes or the create result, comma-separated
547			 */
548
549			if (conn->ss) {
550				rsp = conn->ss->policy;
551
552				while (rsp) {
553					if (n != 4 && n < (int)sizeof(s) - 2)
554						s[n++] = ',';
555					n += lws_snprintf(&s[n], sizeof(s) - (unsigned int)n,
556							"%s", rsp->streamtype);
557					rsp = lws_ss_policy_lookup(wsi->a.context,
558						rsp->rideshare_streamtype);
559				}
560			}
561			s[2] = (char)(n - 3);
562			conn->state = LPCSPROX_OPERATIONAL;
563			lws_set_timeout(wsi, 0, 0);
564			break;
565
566		case LPCSPROX_OPERATIONAL:
567
568			/*
569			 * returning [onward -> ] proxy]-> client
570			 * rx metadata has priority 1
571			 */
572
573			md = conn->ss->metadata;
574			while (md) {
575				// lwsl_notice("%s: check %s: %d\n", __func__,
576				// md->name, md->pending_onward);
577				if (md->pending_onward) {
578					size_t naml = strlen(md->name);
579
580					// lwsl_notice("%s: proxy issuing rxmd\n", __func__);
581
582					if (4 + naml + md->length > sizeof(s)) {
583						lwsl_err("%s: rxmdata too big\n",
584								__func__);
585						goto hangup;
586					}
587					md->pending_onward = 0;
588					p = (uint8_t *)s;
589					p[0] = LWSSS_SER_RXPRE_METADATA;
590					lws_ser_wu16be(&p[1], (uint16_t)(1 + naml +
591							      md->length));
592					p[3] = (uint8_t)naml;
593					memcpy(&p[4], md->name, naml);
594					p += 4 + naml;
595					memcpy(p, md->value__may_own_heap,
596					       md->length);
597					p += md->length;
598
599					n = lws_ptr_diff(p, cp);
600					goto again;
601				}
602
603				md = md->next;
604			}
605
606			/*
607			 * If we have performance data, render it in JSON
608			 * and send that in LWSSS_SER_RXPRE_PERF has
609			 * priority 2
610			 */
611
612#if defined(LWS_WITH_CONMON)
613			if (conn->ss->conmon_json) {
614				unsigned int xlen = conn->ss->conmon_len;
615
616				if (xlen > sizeof(s) - 3)
617					xlen = sizeof(s) - 3;
618				cp = (uint8_t *)s;
619				p = (uint8_t *)s;
620				p[0] = LWSSS_SER_RXPRE_PERF;
621				lws_ser_wu16be(&p[1], (uint16_t)xlen);
622				memcpy(&p[3], conn->ss->conmon_json, xlen);
623
624				lws_free_set_NULL(conn->ss->conmon_json);
625				n = (int)(xlen + 3);
626
627				pay = 0;
628				goto again;
629			}
630#endif
631			/*
632			 * if no fresh rx metadata, just pass through incoming
633			 * dsh
634			 */
635
636			if (lws_dsh_get_head(conn->dsh, KIND_SS_TO_P,
637					     (void **)&p, &si))
638				break;
639
640			cp = p;
641
642#if 0
643			if (cp[0] == LWSSS_SER_RXPRE_RX_PAYLOAD &&
644			    wsi->a.context->detailed_latency_cb) {
645
646				/*
647				 * we're fulfilling rx that came in on ss
648				 * by sending it back out to the client on
649				 * the Unix Domain Socket
650				 *
651				 * +  7  u32  write will compute latency here...
652				 * + 11  u32  ust we received from ss
653				 *
654				 * lws_write will report it and fill in
655				 * LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE
656				 */
657
658				us = lws_now_usecs();
659				lws_ser_wu32be(&p[7], us -
660						      lws_ser_ru64be(&p[11]));
661				lws_ser_wu64be(&p[11], us);
662
663				wsi->detlat.acc_size =
664					wsi->detlat.req_size = si - 19;
665				/* time proxy held it */
666				wsi->detlat.latencies[
667				            LAT_DUR_PROXY_RX_TO_ONWARD_TX] =
668							lws_ser_ru32be(&p[7]);
669			}
670#endif
671			pay = 1;
672			n = (int)si;
673			break;
674		default:
675			break;
676		}
677again:
678		if (!n)
679			break;
680
681		if (lws_fi(&wsi->fic, "ssproxy_client_write_fail"))
682			n = -1;
683		else
684			n = lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);
685		if (n < 0) {
686			lwsl_info("%s: WRITEABLE: %d\n", __func__, n);
687
688			goto hangup;
689		}
690
691		switch (conn->state) {
692		case LPCSPROX_REPORTING_FAIL:
693			goto hangup;
694		case LPCSPROX_OPERATIONAL:
695			if (!conn)
696				break;
697			if (pay) {
698				lws_dsh_free((void **)&p);
699
700				/*
701				 * Did we go below the rx flow threshold for
702				 * this dsh?
703				 */
704
705				if (conn->onward_in_flow_control &&
706				    conn->ss->policy->proxy_buflen_rxflow_on_above &&
707				    conn->ss->wsi &&
708				    lws_dsh_get_size(conn->dsh, KIND_SS_TO_P) <
709				      conn->ss->policy->proxy_buflen_rxflow_off_below) {
710					lwsl_info("%s: %s: rxflow enabling rx (%lu / %lu, lwm %lu)\n", __func__,
711							lws_wsi_tag(conn->ss->wsi),
712							(unsigned long)lws_dsh_get_size(conn->dsh, KIND_SS_TO_P),
713							(unsigned long)conn->ss->policy->proxy_buflen,
714							(unsigned long)conn->ss->policy->proxy_buflen_rxflow_off_below);
715					/*
716					 * Resume receiving taking in rx once
717					 * below the low threshold
718					 */
719					lws_rx_flow_control(conn->ss->wsi,
720							    LWS_RXFLOW_ALLOW);
721					conn->onward_in_flow_control = 0;
722				}
723			}
724			if (!lws_dsh_get_head(conn->dsh, KIND_SS_TO_P,
725					     (void **)&p, &si)) {
726				if (!lws_send_pipe_choked(wsi)) {
727					cp = p;
728					pay = 1;
729					n = (int)si;
730					goto again;
731				}
732				lws_callback_on_writable(wsi);
733			}
734			break;
735		default:
736			break;
737		}
738		break;
739
740	default:
741		break;
742	}
743
744	return lws_callback_http_dummy(wsi, reason, user, in, len);
745
746hangup:
747	/* hang up on him */
748
749	return -1;
750}
751
752static const struct lws_protocols protocols[] = {
753	{
754		"ssproxy-protocol",
755		callback_ss_proxy,
756		sizeof(struct raw_pss),
757		2048, 2048, NULL, 0
758	},
759	{ NULL, NULL, 0, 0, 0, NULL, 0 }
760};
761
762/*
763 * called from create_context()
764 */
765
766int
767lws_ss_proxy_create(struct lws_context *context, const char *bind, int port)
768{
769	struct lws_context_creation_info info;
770
771	memset(&info, 0, sizeof(info));
772
773	info.vhost_name			= "ssproxy";
774	info.options = LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG |
775			LWS_SERVER_OPTION_SS_PROXY;
776	info.port = port;
777	if (!port) {
778		if (!bind)
779#if defined(__linux__)
780			bind = "@proxy.ss.lws";
781#else
782			bind = "/tmp/proxy.ss.lws";
783#endif
784		info.options |= LWS_SERVER_OPTION_UNIX_SOCK;
785	}
786	info.iface			= bind;
787#if defined(__linux__)
788	info.unix_socket_perms		= "root:root";
789#else
790#endif
791	info.listen_accept_role		= "raw-skt";
792	info.listen_accept_protocol	= "ssproxy-protocol";
793	info.protocols			= protocols;
794
795	if (!lws_create_vhost(context, &info)) {
796		lwsl_err("%s: Failed to create ss proxy vhost\n", __func__);
797
798		return 1;
799	}
800
801	return 0;
802}
803