• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lws-minimal-secure-streams-client
3  *
4  * Written in 2010-2020 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  *
10  * This client does not perform any INET networking... instead it opens a unix
11  * domain socket on a proxy that is listening for it, and that creates the
12  * actual secure stream connection.
13  *
14  * We are able to use the usual secure streams api in the client process, with
15  * payloads and connection state information proxied over the unix domain
16  * socket and fulfilled in the proxy process.
17  *
18  * The public client helper pieces are built as part of lws
19  */
20 #include <private-lib-core.h>
21 
22 extern const uint32_t ss_state_txn_validity[17];
23 
24 int
lws_ss_check_next_state_sspc(lws_sspc_handle_t * ss,uint8_t * prevstate,lws_ss_constate_t cs)25 lws_ss_check_next_state_sspc(lws_sspc_handle_t *ss, uint8_t *prevstate,
26 			     lws_ss_constate_t cs)
27 {
28 	if (cs >= LWSSSCS_USER_BASE || cs == LWSSSCS_EVENT_WAIT_CANCELLED)
29 		/*
30 		 * we can't judge user or transient states, leave the old state
31 		 * and just wave them through
32 		 */
33 		return 0;
34 
35 	if (cs >= LWS_ARRAY_SIZE(ss_state_txn_validity)) {
36 		/* we don't recognize this state as usable */
37 		lwsl_sspc_err(ss, "bad new state %u", cs);
38 		assert(0);
39 		return 1;
40 	}
41 
42 	if (*prevstate >= LWS_ARRAY_SIZE(ss_state_txn_validity)) {
43 		/* existing state is broken */
44 		lwsl_sspc_err(ss, "bad existing state %u",
45 				(unsigned int)*prevstate);
46 		assert(0);
47 		return 1;
48 	}
49 
50 	if (ss_state_txn_validity[*prevstate] & (1u << cs)) {
51 
52 		lwsl_sspc_notice(ss, "%s -> %s",
53 			       lws_ss_state_name((int)*prevstate),
54 			       lws_ss_state_name((int)cs));
55 
56 		/* this is explicitly allowed, update old state to new */
57 		*prevstate = (uint8_t)cs;
58 
59 		return 0;
60 	}
61 
62 	lwsl_sspc_err(ss, "transition from %s -> %s is illegal",
63 		    lws_ss_state_name((int)*prevstate),
64 		    lws_ss_state_name((int)cs));
65 
66 	assert(0);
67 
68 	return 1;
69 }
70 
71 lws_ss_state_return_t
lws_sspc_event_helper(lws_sspc_handle_t * h,lws_ss_constate_t cs,lws_ss_tx_ordinal_t flags)72 lws_sspc_event_helper(lws_sspc_handle_t *h, lws_ss_constate_t cs,
73 		      lws_ss_tx_ordinal_t flags)
74 {
75 	lws_ss_state_return_t ret;
76 
77 	if (!h)
78 		return LWSSSSRET_OK;
79 
80 	if (lws_ss_check_next_state_sspc(h, &h->prev_ss_state, cs))
81 		return LWSSSSRET_DESTROY_ME;
82 
83 	if (!h->ssi.state)
84 		return LWSSSSRET_OK;
85 
86 	h->h_in_svc = h;
87 	ret = h->ssi.state((void *)((uint8_t *)&h[1]), NULL, cs, flags);
88 	h->h_in_svc = NULL;
89 
90 	return ret;
91 }
92 
93 static void
lws_sspc_sul_retry_cb(lws_sorted_usec_list_t * sul)94 lws_sspc_sul_retry_cb(lws_sorted_usec_list_t *sul)
95 {
96 	lws_sspc_handle_t *h = lws_container_of(sul, lws_sspc_handle_t, sul_retry);
97 	static struct lws_client_connect_info i;
98 
99 	/*
100 	 * We may have started up before the system proxy, so be prepared with
101 	 * a sul to retry at 1Hz
102 	 */
103 
104 	memset(&i, 0, sizeof i);
105 	i.context = h->context;
106 	if (h->context->ss_proxy_port) { /* tcp */
107 		i.address = h->context->ss_proxy_address;
108 		i.port = h->context->ss_proxy_port;
109 		i.iface = h->context->ss_proxy_bind;
110 	} else {
111 		if (h->context->ss_proxy_bind)
112 			i.address = h->context->ss_proxy_bind;
113 		else
114 #if defined(__linux__)
115 			i.address = "+@proxy.ss.lws";
116 #else
117 			i.address = "+/tmp/proxy.ss.lws";
118 #endif
119 	}
120 	i.host = i.address;
121 	i.origin = i.address;
122 	i.method = "RAW";
123 	i.protocol = lws_sspc_protocols[0].name;
124 	i.local_protocol_name = lws_sspc_protocols[0].name;
125 	i.path = "";
126 	i.pwsi = &h->cwsi;
127 	i.opaque_user_data = (void *)h;
128 	i.ssl_connection = LCCSCF_SECSTREAM_PROXY_LINK;
129 
130 	lws_metrics_caliper_bind(h->cal_txn, h->context->mt_ss_cliprox_conn);
131 #if defined(LWS_WITH_SYS_METRICS)
132 	lws_metrics_tag_add(&h->cal_txn.mtags_owner, "ss", h->ssi.streamtype);
133 #endif
134 
135 	/* this wsi is the link to the proxy */
136 
137 	if (!lws_client_connect_via_info(&i)) {
138 
139 #if defined(LWS_WITH_SYS_METRICS)
140 		/*
141 		 * If any hanging caliper measurement, dump it, and free any tags
142 		 */
143 		lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL);
144 #endif
145 
146 		lws_sul_schedule(h->context, 0, &h->sul_retry,
147 				 lws_sspc_sul_retry_cb, LWS_US_PER_SEC);
148 
149 		return;
150 	}
151 
152 	lwsl_sspc_notice(h, "%s", h->cwsi->lc.gutag);
153 }
154 
155 static int
lws_sspc_serialize_metadata(lws_sspc_handle_t * h,lws_sspc_metadata_t * md,uint8_t * p,uint8_t * end)156 lws_sspc_serialize_metadata(lws_sspc_handle_t *h, lws_sspc_metadata_t *md,
157 				uint8_t *p, uint8_t *end)
158 {
159 	int n, txc;
160 
161 	if (md->name[0] == '\0') {
162 
163 		lwsl_info("sending tx credit update %d\n",
164 				md->tx_cr_adjust);
165 
166 		p[0] = LWSSS_SER_TXPRE_TXCR_UPDATE;
167 		lws_ser_wu16be(&p[1], 4);
168 		lws_ser_wu32be(&p[3], (uint32_t)md->tx_cr_adjust);
169 
170 		n = 7;
171 
172 	} else {
173 
174 		lwsl_sspc_info(h, "sending metadata");
175 
176 		p[0] = LWSSS_SER_TXPRE_METADATA;
177 		txc = (int)strlen(md->name);
178 		n = txc + 1 + (int)md->len;
179 		if (n > 0xffff)
180 			/* we can't serialize this metadata in 16b length */
181 			return -1;
182 		if (n > lws_ptr_diff(end, &p[4]))
183 			/* we don't have space for this metadata */
184 			return -1;
185 		lws_ser_wu16be(&p[1], (uint16_t)n);
186 		p[3] = (uint8_t)txc;
187 		memcpy(&p[4], md->name, (unsigned int)txc);
188 		memcpy(&p[4 + txc], &md[1], md->len);
189 		n = 4 + txc + (int)md->len;
190 	}
191 
192 	lws_dll2_remove(&md->list);
193 	lws_free(md);
194 
195 	return n;
196 }
197 
198 static int
callback_sspc_client(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)199 callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
200 		     void *user, void *in, size_t len)
201 {
202 	lws_sspc_handle_t *h = (lws_sspc_handle_t *)lws_get_opaque_user_data(wsi);
203 	size_t pktsize = wsi->a.context->max_http_header_data;
204 	void *m = (void *)((uint8_t *)&h[1]);
205 	uint8_t *pkt = NULL, *p = NULL, *end = NULL;
206 	lws_ss_state_return_t r;
207 	uint64_t interval;
208 	const uint8_t *cp;
209 	uint8_t s[64];
210 	lws_usec_t us;
211 	int flags, n;
212 
213 	switch (reason) {
214 
215 	case LWS_CALLBACK_CONNECTING:
216 		/*
217 		 * In our particular case, we want CCEs even inside the
218 		 * initial connect loop time
219 		 */
220 		wsi->client_suppress_CONNECTION_ERROR = 0;
221 		break;
222 
223 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
224 		lwsl_warn("%s: CCE: %s\n", __func__,
225 			  in ? (const char *)in : "null");
226 #if defined(LWS_WITH_SYS_METRICS)
227 		/*
228 		 * If any hanging caliper measurement, dump it, and free any tags
229 		 */
230 		lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL);
231 #endif
232 		lws_set_opaque_user_data(wsi, NULL);
233 		h->cwsi = NULL;
234 		lws_sul_schedule(h->context, 0, &h->sul_retry,
235 				 lws_sspc_sul_retry_cb, LWS_US_PER_SEC);
236 		if (h->ssi.state) {
237 			interval = (uint64_t)(lws_now_usecs() - h->us_start_upstream) /
238 								LWS_US_PER_MS;
239 			if (interval > 0xffffffffull)
240 				interval = 0xffffffffull;
241 			r = h->ssi.state(lws_sspc_to_user_object(h), NULL,
242 					  LWSSSCS_UPSTREAM_LINK_RETRY,
243 					  (uint32_t)interval);
244 			if (r == LWSSSSRET_DESTROY_ME)
245 				lws_sspc_destroy(&h);
246 		}
247 		break;
248 
249         case LWS_CALLBACK_RAW_CONNECTED:
250 		if (!h || lws_fi(&h->fic, "sspc_fail_on_linkup"))
251 			return -1;
252 		lwsl_sspc_info(h, "CONNECTED (%s)", h->ssi.streamtype);
253 
254 		h->state = LPCSCLI_SENDING_INITIAL_TX;
255 		/*
256 		 * We create the dsh at the response to the initial tx, which
257 		 * will let us know the policy's max size for it... let's
258 		 * protect the connection with a promise to complete the
259 		 * SS serialization streamtype negotation within a short period,
260 		 * we will cancel this timeout when we have the proxy's ack
261 		 * of the streamtype serialization, eg, it exists in the proxy
262 		 * policy etc
263 		 */
264 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, 3);
265 		lws_callback_on_writable(wsi);
266 		h->us_start_upstream = 0;
267                 break;
268 
269 	case LWS_CALLBACK_RAW_CLOSE:
270 		/*
271 		 * our ss proxy Unix Domain socket has closed...
272 		 */
273 		if (!h) {
274 			lwsl_info("%s: no sspc on client proxy link close", __func__);
275 			break;
276 		}
277 		lwsl_sspc_info(h, "LWS_CALLBACK_RAW_CLOSE: proxy conn down, wsi %s",
278 				lws_wsi_tag(wsi));
279 
280 		lws_dsh_destroy(&h->dsh);
281 		if (h->ss_dangling_connected && h->ssi.state) {
282 
283 			lwsl_sspc_notice(h, "setting _DISCONNECTED");
284 			h->ss_dangling_connected = 0;
285 			h->prev_ss_state = LWSSSCS_DISCONNECTED;
286 			r = h->ssi.state(ss_to_userobj(h), NULL,
287 						 LWSSSCS_DISCONNECTED, 0);
288 			if (r == LWSSSSRET_DESTROY_ME) {
289 				h->cwsi = NULL;
290 				lws_set_opaque_user_data(wsi, NULL);
291 				lws_sspc_destroy(&h);
292 				break;
293 			}
294 		}
295 
296 		h->cwsi = NULL;
297 		/*
298 		 * schedule a reconnect in 1s
299 		 */
300 		lws_sul_schedule(h->context, 0, &h->sul_retry,
301 				 lws_sspc_sul_retry_cb, LWS_US_PER_SEC);
302 
303 		break;
304 
305 	case LWS_CALLBACK_RAW_RX:
306 		/*
307 		 * ie, the proxy has sent us something
308 		 */
309 
310 		if (!h || !h->cwsi) {
311 			lwsl_info("%s: rx when client ss destroyed\n", __func__);
312 
313 			return -1;
314 		}
315 
316 		lwsl_sspc_info(h, "%s: RAW_RX: rx %d\n", __func__, (int)len);
317 
318 		if (!len) {
319 			lwsl_sspc_notice(h, "RAW_RX: zero len");
320 
321 			return -1;
322 		}
323 
324 		if (lws_fi(&h->fic, "sspc_fake_rxparse_disconnect_me"))
325 			n = LWSSSSRET_DISCONNECT_ME;
326 		else
327 			if (lws_fi(&h->fic, "sspc_fake_rxparse_destroy_me"))
328 				n = LWSSSSRET_DESTROY_ME;
329 			else
330 				n = lws_ss_deserialize_parse(&h->parser,
331 							     lws_get_context(wsi),
332 							     h->dsh, in, len,
333 							     &h->state, h,
334 							     (lws_ss_handle_t **)m,
335 							     &h->ssi, 1);
336 		switch (n) {
337 		case LWSSSSRET_OK:
338 			break;
339 		case LWSSSSRET_DISCONNECT_ME:
340 			lwsl_info("%s: proxlicent RX ended with DISCONNECT_ME\n",
341 					__func__);
342 			return -1;
343 		case LWSSSSRET_DESTROY_ME:
344 			lwsl_info("%s: proxlicent RX ended with DESTROY_ME\n",
345 					__func__);
346 			lws_set_opaque_user_data(wsi, NULL);
347 			lws_sspc_destroy(&h);
348 			return -1;
349 		}
350 
351 		if (h->state == LPCSCLI_LOCAL_CONNECTED ||
352 		    h->state == LPCSCLI_ONWARD_CONNECT)
353 			lws_set_timeout(wsi, 0, 0);
354 
355 		break;
356 
357 	case LWS_CALLBACK_RAW_WRITEABLE:
358 
359 		/*
360 		 * We can transmit something to the proxy...
361 		 */
362 
363 		if (!h)
364 			break;
365 
366 		lwsl_sspc_debug(h, "WRITEABLE %s, state %d",
367 				wsi->lc.gutag, h->state);
368 
369 		/*
370 		 * Management of ss timeout can happen any time and doesn't
371 		 * depend on wsi existence or state
372 		 */
373 
374 		n = 0;
375 		cp = s;
376 
377 		if (h->pending_timeout_update) {
378 			s[0] = LWSSS_SER_TXPRE_TIMEOUT_UPDATE;
379 			s[1] = 0;
380 			s[2] = 4;
381 			/*
382 			 *          0: use policy timeout value
383 			 * 0xffffffff: cancel the timeout
384 			 */
385 			lws_ser_wu32be(&s[3], h->timeout_ms);
386 			/* in case anything else to write */
387 			lws_callback_on_writable(h->cwsi);
388 			h->pending_timeout_update = 0;
389 			n = 7;
390 			goto do_write;
391 		}
392 
393 		s[1] = 0;
394 		/*
395 		 * This is the state of the link that connects us to the onward
396 		 * proxy
397 		 */
398 		switch (h->state) {
399 		case LPCSCLI_SENDING_INITIAL_TX:
400 			/*
401 			 * We are negotating the opening of a particular
402 			 * streamtype
403 			 */
404 			n = (int)strlen(h->ssi.streamtype) + 1 + 4 + 4;
405 
406 			s[0] = LWSSS_SER_TXPRE_STREAMTYPE;
407 			lws_ser_wu16be(&s[1], (uint16_t)n);
408 			/* SSSv1: add protocol version byte (initially 1) */
409 			s[3] = (uint8_t)LWS_SSS_CLIENT_PROTOCOL_VERSION;
410 			lws_ser_wu32be(&s[4], (uint32_t)getpid());
411 			lws_ser_wu32be(&s[8], (uint32_t)h->txc.peer_tx_cr_est);
412 			//h->txcr_out = txc;
413 			lws_strncpy((char *)&s[12], h->ssi.streamtype, sizeof(s) - 12);
414 			n += 3;
415 			h->state = LPCSCLI_WAITING_CREATE_RESULT;
416 
417 			break;
418 
419 		case LPCSCLI_LOCAL_CONNECTED:
420 
421 			// lwsl_notice("%s: LPCSCLI_LOCAL_CONNECTED\n", __func__);
422 
423 			/*
424 			 * Do we need to prioritize sending any metadata
425 			 * changes?
426 			 */
427 
428 			if (h->metadata_owner.count) {
429 				lws_sspc_metadata_t *md = lws_container_of(
430 					lws_dll2_get_tail(&h->metadata_owner),
431 					lws_sspc_metadata_t, list);
432 
433 				pkt = lws_malloc(pktsize + LWS_PRE, __func__);
434 				if (!pkt)
435 					goto hangup;
436 				cp = p = pkt + LWS_PRE;
437 				end = p + pktsize;
438 
439 				n = lws_sspc_serialize_metadata(h, md, p, end);
440 				if (n < 0)
441 					goto metadata_hangup;
442 
443 				lwsl_sspc_debug(h, "(local_conn) metadata");
444 
445 				goto req_write_and_issue;
446 			}
447 
448 			if (h->pending_writeable_len) {
449 				lwsl_sspc_debug(h, "(local_conn) PAYLOAD_LENGTH_HINT %u",
450 					   (unsigned int)h->writeable_len);
451 				s[0] = LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT;
452 				lws_ser_wu16be(&s[1], 4);
453 				lws_ser_wu32be(&s[3], (uint32_t)h->writeable_len);
454 				h->pending_writeable_len = 0;
455 				n = 7;
456 				goto req_write_and_issue;
457 			}
458 
459 			if (h->conn_req_state >= LWSSSPC_ONW_ONGOING) {
460 				lwsl_sspc_info(h, "conn_req_state %d",
461 						h->conn_req_state);
462 				break;
463 			}
464 
465 			lwsl_sspc_info(h, "(local_conn) onward connect");
466 
467 			h->conn_req_state = LWSSSPC_ONW_ONGOING;
468 
469 			s[0] = LWSSS_SER_TXPRE_ONWARD_CONNECT;
470 			s[1] = 0;
471 			s[2] = 0;
472 			n = 3;
473 			break;
474 
475 		case LPCSCLI_OPERATIONAL:
476 
477 			/*
478 			 *
479 			 * - Do we need to prioritize sending any metadata
480 			 *   changes?  (includes txcr updates)
481 			 *
482 			 * - Do we need to forward a hint about the payload
483 			 *   length?
484 			 */
485 
486 			pkt = lws_malloc(pktsize + LWS_PRE, __func__);
487 			if (!pkt)
488 				goto hangup;
489 			cp = p = pkt + LWS_PRE;
490 			end = p + pktsize;
491 
492 			if (h->metadata_owner.count) {
493 				lws_sspc_metadata_t *md = lws_container_of(
494 					lws_dll2_get_tail(&h->metadata_owner),
495 					lws_sspc_metadata_t, list);
496 
497 				n = lws_sspc_serialize_metadata(h, md, p, end);
498 				if (n < 0)
499 					goto metadata_hangup;
500 
501 				goto req_write_and_issue;
502 			}
503 
504 			if (h->pending_writeable_len) {
505 				lwsl_sspc_info(h, "PAYLOAD_LENGTH_HINT %u",
506 					  (unsigned int)h->writeable_len);
507 				s[0] = LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT;
508 				lws_ser_wu16be(&s[1], 4);
509 				lws_ser_wu32be(&s[3], (uint32_t)h->writeable_len);
510 				h->pending_writeable_len = 0;
511 				n = 7;
512 				goto req_write_and_issue;
513 			}
514 
515 			/* we can't write anything if we don't have credit */
516 			if (!h->ignore_txc && h->txc.tx_cr <= 0) {
517 				lwsl_sspc_info(h, "WRITEABLE / OPERATIONAL:"
518 					    " lack credit (%d)",
519 					    h->txc.tx_cr);
520 				// break;
521 			}
522 
523 			len = pktsize - LWS_PRE - 19;
524 			flags = 0;
525 			if (!h->ssi.tx) {
526 				n = 0;
527 				goto do_write_nz;
528 			}
529 
530 			n = h->ssi.tx(m, h->ord++, pkt + LWS_PRE + 19, &len,
531 				      &flags);
532 			switch (n) {
533 			case LWSSSSRET_TX_DONT_SEND:
534 				n = 0;
535 				goto do_write_nz;
536 
537 			case LWSSSSRET_DISCONNECT_ME:
538 			case LWSSSSRET_DESTROY_ME:
539 				lwsl_notice("%s: sspc tx DISCONNECT/DESTROY unimplemented\n", __func__);
540 				break;
541 			default:
542 				break;
543 			}
544 
545 			h->txc.tx_cr = h->txc.tx_cr - (int)len;
546 
547 			cp = p;
548 			n = (int)(len + 19);
549 			us = lws_now_usecs();
550 			p[0] = LWSSS_SER_TXPRE_TX_PAYLOAD;
551 			lws_ser_wu16be(&p[1], (uint16_t)(len + 19 - 3));
552 			lws_ser_wu32be(&p[3], (uint32_t)flags);
553 			/* time spent here waiting to send this */
554 			lws_ser_wu32be(&p[7], (uint32_t)(us - h->us_earliest_write_req));
555 			/* ust that the client write happened */
556 			lws_ser_wu64be(&p[11], (uint64_t)us);
557 			h->us_earliest_write_req = 0;
558 
559 			if (flags & LWSSS_FLAG_EOM)
560 				if (h->rsidx + 1 < (int)LWS_ARRAY_SIZE(h->rideshare_ofs) &&
561 				    h->rideshare_ofs[h->rsidx + 1])
562 					h->rsidx++;
563 
564 			break;
565 		default:
566 			break;
567 		}
568 
569 do_write_nz:
570 
571 		if (!n)
572 			break;
573 
574 do_write:
575 		if (lws_fi(&h->fic, "sspc_link_write_fail"))
576 			n = -1;
577 		else
578 			n = lws_write(wsi, (uint8_t *)cp, (unsigned int)n, LWS_WRITE_RAW);
579 		if (n < 0) {
580 			lwsl_sspc_notice(h, "WRITEABLE: %d", n);
581 
582 			goto hangup;
583 		}
584 		break;
585 
586 	default:
587 		break;
588 	}
589 
590 	lws_free(pkt);
591 
592 	return lws_callback_http_dummy(wsi, reason, user, in, len);
593 
594 metadata_hangup:
595 	lwsl_sspc_err(h, "metadata too large");
596 
597 hangup:
598 	lws_free(pkt);
599 	lwsl_warn("hangup\n");
600 	/* hang up on him */
601 	return -1;
602 
603 req_write_and_issue:
604 	/* in case anything else to write */
605 	lws_callback_on_writable(h->cwsi);
606 	goto do_write_nz;
607 }
608 
609 const struct lws_protocols lws_sspc_protocols[] = {
610 	{
611 		"ssproxy-protocol",
612 		callback_sspc_client,
613 		0,
614 		2048, 2048, NULL, 0
615 	},
616 	{ NULL, NULL, 0, 0, 0, NULL, 0 }
617 };
618 
619 int
lws_sspc_create(struct lws_context * context,int tsi,const lws_ss_info_t * ssi,void * opaque_user_data,lws_sspc_handle_t ** ppss,struct lws_sequencer * seq_owner,const char ** ppayload_fmt)620 lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
621 	        void *opaque_user_data, lws_sspc_handle_t **ppss,
622 	        struct lws_sequencer *seq_owner, const char **ppayload_fmt)
623 {
624 	lws_sspc_handle_t *h;
625 	uint8_t *ua;
626 	char *p;
627 
628 	lws_service_assert_loop_thread(context, tsi);
629 
630 	/* allocate the handle (including ssi), the user alloc,
631 	 * and the streamname */
632 
633 	h = malloc(sizeof(lws_sspc_handle_t) + ssi->user_alloc +
634 				strlen(ssi->streamtype) + 1);
635 	if (!h)
636 		return 1;
637 	memset(h, 0, sizeof(*h));
638 
639 	h->lc.log_cx = context->log_cx;
640 
641 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
642 	h->fic.name = "sspc";
643 	lws_xos_init(&h->fic.xos, lws_xos(&context->fic.xos));
644 	if (ssi->fic.fi_owner.count)
645 		lws_fi_import(&h->fic, &ssi->fic);
646 
647 	lws_fi_inherit_copy(&h->fic, &context->fic, "ss", ssi->streamtype);
648 #endif
649 
650 	if (lws_fi(&h->fic, "sspc_create_oom")) {
651 		/*
652 		 * We have to do this a litte later, so we can cleanly inherit
653 		 * the OOM pieces and drain the info fic
654 		 */
655 		lws_fi_destroy(&h->fic);
656 		free(h);
657 		return 1;
658 	}
659 
660 	__lws_lc_tag(context, &context->lcg[LWSLCG_SSP_CLIENT], &h->lc,
661 			ssi->streamtype);
662 
663 	memcpy(&h->ssi, ssi, sizeof(*ssi));
664 	ua = (uint8_t *)&h[1];
665 	memset(ua, 0, ssi->user_alloc);
666 	p = (char *)ua + ssi->user_alloc;
667 	memcpy(p, ssi->streamtype, strlen(ssi->streamtype) + 1);
668 	h->ssi.streamtype = (const char *)p;
669 	h->context = context;
670 	h->us_start_upstream = lws_now_usecs();
671 
672 	if (!ssi->manual_initial_tx_credit)
673 		h->txc.peer_tx_cr_est = 500000000;
674 	else
675 		h->txc.peer_tx_cr_est = ssi->manual_initial_tx_credit;
676 
677 	if (!strcmp(ssi->streamtype, LWS_SMD_STREAMTYPENAME))
678 		h->ignore_txc = 1;
679 
680 	lws_dll2_add_head(&h->client_list, &context->pt[tsi].ss_client_owner);
681 
682 	/* fill in the things the real api does for the caller */
683 
684 	*((void **)(ua + ssi->opaque_user_data_offset)) = opaque_user_data;
685 	*((void **)(ua + ssi->handle_offset)) = h;
686 
687 	if (ppss)
688 		*ppss = h;
689 
690 	/* try the actual connect */
691 
692 	lws_sspc_sul_retry_cb(&h->sul_retry);
693 
694 	return 0;
695 }
696 
697 /* used on context destroy when iterating listed lws_ss on a pt */
698 
699 int
lws_sspc_destroy_dll(struct lws_dll2 * d,void * user)700 lws_sspc_destroy_dll(struct lws_dll2 *d, void *user)
701 {
702 	lws_sspc_handle_t *h = lws_container_of(d, lws_sspc_handle_t, client_list);
703 
704 	lws_sspc_destroy(&h);
705 
706 	return 0;
707 }
708 
709 void
lws_sspc_rxmetadata_destroy(lws_sspc_handle_t * h)710 lws_sspc_rxmetadata_destroy(lws_sspc_handle_t *h)
711 {
712 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
713 			lws_dll2_get_head(&h->metadata_owner_rx)) {
714 		lws_sspc_metadata_t *md =
715 				lws_container_of(d, lws_sspc_metadata_t, list);
716 
717 		lws_dll2_remove(&md->list);
718 		lws_free(md);
719 
720 	} lws_end_foreach_dll_safe(d, d1);
721 }
722 
723 void
lws_sspc_destroy(lws_sspc_handle_t ** ph)724 lws_sspc_destroy(lws_sspc_handle_t **ph)
725 {
726 	lws_sspc_handle_t *h;
727 
728 	if (!*ph)
729 		return;
730 
731 	h = *ph;
732 	if (h == h->h_in_svc) {
733 		lwsl_err("%s: illegal destroy, return LWSSSSRET_DESTROY_ME instead\n",
734 				__func__);
735 		assert(0);
736 		return;
737 	}
738 
739 	lws_service_assert_loop_thread(h->context, 0);
740 
741 	if (h->destroying)
742 		return;
743 
744 	h->destroying = 1;
745 
746 	/* if this caliper is still dangling at destroy, we failed */
747 #if defined(LWS_WITH_SYS_METRICS)
748 	/*
749 	 * If any hanging caliper measurement, dump it, and free any tags
750 	 */
751 	lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL);
752 #endif
753 	if (h->ss_dangling_connected && h->ssi.state) {
754 		lws_sspc_event_helper(h, LWSSSCS_DISCONNECTED, 0);
755 		h->ss_dangling_connected = 0;
756 	}
757 
758 #if defined(LWS_WITH_SYS_FAULT_INJECTION)
759 	lws_fi_destroy(&h->fic);
760 #endif
761 
762 	lws_sul_cancel(&h->sul_retry);
763 	lws_dll2_remove(&h->client_list);
764 
765 	if (h->dsh)
766 		lws_dsh_destroy(&h->dsh);
767 	if (h->cwsi) {
768 		lws_set_opaque_user_data(h->cwsi, NULL);
769 		lws_wsi_close(h->cwsi, LWS_TO_KILL_ASYNC);
770 		h->cwsi = NULL;
771 	}
772 
773 	/* clean out any pending metadata changes that didn't make it */
774 
775 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
776 			lws_dll2_get_head(&(*ph)->metadata_owner)) {
777 		lws_sspc_metadata_t *md =
778 				lws_container_of(d, lws_sspc_metadata_t, list);
779 
780 		lws_dll2_remove(&md->list);
781 		lws_free(md);
782 
783 	} lws_end_foreach_dll_safe(d, d1);
784 
785 	lws_sspc_rxmetadata_destroy(h);
786 
787 	lws_sspc_event_helper(h, LWSSSCS_DESTROYING, 0);
788 	*ph = NULL;
789 
790 	lws_sul_cancel(&h->sul_retry);
791 
792 
793 	/* confirm no sul left scheduled in handle or user allocation object */
794 	lws_sul_debug_zombies(h->context, h, sizeof(*h) + h->ssi.user_alloc,
795 			      __func__);
796 
797 	__lws_lc_untag(h->context, &h->lc);
798 
799 	free(h);
800 }
801 
802 lws_ss_state_return_t
lws_sspc_request_tx(lws_sspc_handle_t * h)803 lws_sspc_request_tx(lws_sspc_handle_t *h)
804 {
805 	if (!h || !h->cwsi)
806 		return LWSSSSRET_OK;
807 
808 	lws_service_assert_loop_thread(h->context, 0);
809 
810 	if (!h->us_earliest_write_req)
811 		h->us_earliest_write_req = lws_now_usecs();
812 
813 	if (h->state == LPCSCLI_LOCAL_CONNECTED &&
814 	    h->conn_req_state == LWSSSPC_ONW_NONE)
815 		h->conn_req_state = LWSSSPC_ONW_REQ;
816 
817 	lws_callback_on_writable(h->cwsi);
818 
819 	return LWSSSSRET_OK;
820 }
821 
822 /*
823  * Currently we fulfil the writeable part locally by just enabling POLLOUT on
824  * the UDS link, without serialization footprint, which is reasonable as far as
825  * it goes.
826  *
827  * But for the ..._len() variant, the expected payload length hint we are being
828  * told is something that must be serialized to the onward peer, since either
829  * that guy or someone upstream of him is the guy who will compose the framing
830  * with it that actually goes out.
831  *
832  * This information is needed at the upstream guy before we have sent any
833  * payload, eg, for http POST, he has to prepare the content-length in the
834  * headers, before any payload.  So we have to issue a serialization of the
835  * length at this point.
836  */
837 
838 lws_ss_state_return_t
lws_sspc_request_tx_len(lws_sspc_handle_t * h,unsigned long len)839 lws_sspc_request_tx_len(lws_sspc_handle_t *h, unsigned long len)
840 {
841 	/*
842 	 * for client conns, they cannot even complete creation of the handle
843 	 * without the onwared connection to the proxy, it's not legal to start
844 	 * using it until it's operation and has the onward connection (and the
845 	 * link has called CREATED state)
846 	 */
847 
848 	if (!h)
849 		return LWSSSSRET_OK;
850 
851 	lws_service_assert_loop_thread(h->context, 0);
852 
853 	lwsl_sspc_notice(h, "setting writeable_len %u", (unsigned int)len);
854 	h->writeable_len = len;
855 	h->pending_writeable_len = 1;
856 
857 	if (!h->us_earliest_write_req)
858 		h->us_earliest_write_req = lws_now_usecs();
859 
860 	if (h->state == LPCSCLI_LOCAL_CONNECTED &&
861 	    h->conn_req_state == LWSSSPC_ONW_NONE)
862 		h->conn_req_state = LWSSSPC_ONW_REQ;
863 
864 	/*
865 	 * We're going to use this up with serializing h->writeable_len... that
866 	 * will request again.
867 	 */
868 
869 	if (h->cwsi)
870 		lws_callback_on_writable(h->cwsi);
871 
872 	return LWSSSSRET_OK;
873 }
874 
875 int
lws_sspc_client_connect(lws_sspc_handle_t * h)876 lws_sspc_client_connect(lws_sspc_handle_t *h)
877 {
878 	if (!h || h->state == LPCSCLI_OPERATIONAL)
879 		return 0;
880 
881 	lws_service_assert_loop_thread(h->context, 0);
882 
883 	assert(h->state == LPCSCLI_LOCAL_CONNECTED);
884 	if (h->state == LPCSCLI_LOCAL_CONNECTED &&
885 	    h->conn_req_state == LWSSSPC_ONW_NONE)
886 		h->conn_req_state = LWSSSPC_ONW_REQ;
887 	if (h->cwsi)
888 		lws_callback_on_writable(h->cwsi);
889 
890 	return 0;
891 }
892 
893 struct lws_context *
lws_sspc_get_context(struct lws_sspc_handle * h)894 lws_sspc_get_context(struct lws_sspc_handle *h)
895 {
896 	return h->context;
897 }
898 
899 const char *
lws_sspc_rideshare(struct lws_sspc_handle * h)900 lws_sspc_rideshare(struct lws_sspc_handle *h)
901 {
902 	/*
903 	 * ...the serialized RX rideshare name if any...
904 	 */
905 
906 	if (h->parser.rideshare[0]) {
907 		lwsl_sspc_info(h, "parser %s", h->parser.rideshare);
908 
909 		return h->parser.rideshare;
910 	}
911 
912 	/*
913 	 * The tx rideshare index
914 	 */
915 
916 	if (h->rideshare_list[0]) {
917 		lwsl_sspc_info(h, "tx list %s",
918 			  &h->rideshare_list[h->rideshare_ofs[h->rsidx]]);
919 		return &h->rideshare_list[h->rideshare_ofs[h->rsidx]];
920 	}
921 
922 	/*
923 	 * ... otherwise default to our stream type name
924 	 */
925 
926 	lwsl_sspc_info(h, "def %s\n", h->ssi.streamtype);
927 
928 	return h->ssi.streamtype;
929 }
930 
931 static int
_lws_sspc_set_metadata(struct lws_sspc_handle * h,const char * name,const void * value,size_t len,int tx_cr_adjust)932 _lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,
933 		       const void *value, size_t len, int tx_cr_adjust)
934 {
935 	lws_sspc_metadata_t *md;
936 
937 	lws_service_assert_loop_thread(h->context, 0);
938 
939 	/*
940 	 * Are we replacing a pending metadata of the same name?  It's not
941 	 * efficient to do this but user code can do what it likes... let's
942 	 * optimize away the old one.
943 	 *
944 	 * Tx credit adjust always has name ""
945 	 */
946 
947 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
948 				   lws_dll2_get_head(&h->metadata_owner)) {
949 		md = lws_container_of(d, lws_sspc_metadata_t, list);
950 
951 		if (!strcmp(name, md->name)) {
952 			lws_dll2_remove(&md->list);
953 			lws_free(md);
954 			break;
955 		}
956 
957 	} lws_end_foreach_dll_safe(d, d1);
958 
959 	/*
960 	 * We have to stash the metadata and pass it to the proxy
961 	 */
962 
963 	if (lws_fi(&h->fic, "sspc_fail_metadata_set"))
964 		md = NULL;
965 	else
966 		md = lws_malloc(sizeof(*md) + len, "set metadata");
967 	if (!md) {
968 		lwsl_sspc_err(h, "OOM");
969 
970 		return 1;
971 	}
972 
973 	memset(md, 0, sizeof(*md));
974 
975 	md->tx_cr_adjust = tx_cr_adjust;
976 	h->txc.peer_tx_cr_est += tx_cr_adjust;
977 
978 	lws_strncpy(md->name, name, sizeof(md->name));
979 	md->len = len;
980 	if (len)
981 		memcpy(&md[1], value, len);
982 
983 	lws_dll2_add_tail(&md->list, &h->metadata_owner);
984 
985 	if (len) {
986 		lwsl_sspc_info(h, "set metadata %s", name);
987 		lwsl_hexdump_sspc_info(h, value, len);
988 	} else
989 		lwsl_sspc_info(h, "serializing tx cr adj %d",
990 			    (int)tx_cr_adjust);
991 
992 	if (h->cwsi)
993 		lws_callback_on_writable(h->cwsi);
994 
995 	return 0;
996 }
997 
998 int
lws_sspc_set_metadata(struct lws_sspc_handle * h,const char * name,const void * value,size_t len)999 lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,
1000 		      const void *value, size_t len)
1001 {
1002 	return _lws_sspc_set_metadata(h, name, value, len, 0);
1003 }
1004 
1005 int
lws_sspc_get_metadata(struct lws_sspc_handle * h,const char * name,const void ** value,size_t * len)1006 lws_sspc_get_metadata(struct lws_sspc_handle *h, const char *name,
1007 		      const void **value, size_t *len)
1008 {
1009 	lws_sspc_metadata_t *md;
1010 
1011 	/*
1012 	 * client side does not have access to policy
1013 	 * and any metadata are new to it each time,
1014 	 * we allocate them, removing any existing with
1015 	 * the same name first
1016 	 */
1017 
1018 	lws_service_assert_loop_thread(h->context, 0);
1019 
1020 	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1021 			lws_dll2_get_head(&h->metadata_owner_rx)) {
1022 		md = lws_container_of(d,
1023 			   lws_sspc_metadata_t, list);
1024 
1025 		if (!strcmp(md->name, name)) {
1026 			*len = md->len;
1027 			*value = &md[1];
1028 
1029 			return 0;
1030 		}
1031 
1032 	} lws_end_foreach_dll_safe(d, d1);
1033 
1034 	return 1;
1035 }
1036 
1037 int
lws_sspc_add_peer_tx_credit(struct lws_sspc_handle * h,int32_t bump)1038 lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t bump)
1039 {
1040 	lws_service_assert_loop_thread(h->context, 0);
1041 	lwsl_sspc_notice(h, "%d\n", bump);
1042 	return _lws_sspc_set_metadata(h, "", NULL, 0, (int)bump);
1043 }
1044 
1045 int
lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle * h)1046 lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h)
1047 {
1048 	lws_service_assert_loop_thread(h->context, 0);
1049 	return h->txc.peer_tx_cr_est;
1050 }
1051 
1052 void
lws_sspc_start_timeout(struct lws_sspc_handle * h,unsigned int timeout_ms)1053 lws_sspc_start_timeout(struct lws_sspc_handle *h, unsigned int timeout_ms)
1054 {
1055 	lws_service_assert_loop_thread(h->context, 0);
1056 	if (!h->cwsi)
1057 		/* we can't fulfil it */
1058 		return;
1059 	h->timeout_ms = (uint32_t)timeout_ms;
1060 	h->pending_timeout_update = 1;
1061 	lws_callback_on_writable(h->cwsi);
1062 }
1063 
1064 void
lws_sspc_cancel_timeout(struct lws_sspc_handle * h)1065 lws_sspc_cancel_timeout(struct lws_sspc_handle *h)
1066 {
1067 	lws_sspc_start_timeout(h, (unsigned int)-1);
1068 }
1069 
1070 void *
lws_sspc_to_user_object(struct lws_sspc_handle * h)1071 lws_sspc_to_user_object(struct lws_sspc_handle *h)
1072 {
1073 	return (void *)&h[1];
1074 }
1075 
1076 void
lws_sspc_change_handlers(struct lws_sspc_handle * h,lws_ss_state_return_t (* rx)(void * userobj,const uint8_t * buf,size_t len,int flags),lws_ss_state_return_t (* tx)(void * userobj,lws_ss_tx_ordinal_t ord,uint8_t * buf,size_t * len,int * flags),lws_ss_state_return_t (* state)(void * userobj,void * h_src,lws_ss_constate_t state,lws_ss_tx_ordinal_t ack))1077 lws_sspc_change_handlers(struct lws_sspc_handle *h,
1078 	lws_ss_state_return_t (*rx)(void *userobj, const uint8_t *buf, size_t len, int flags),
1079 	lws_ss_state_return_t (*tx)(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
1080 		  size_t *len, int *flags),
1081 	lws_ss_state_return_t (*state)(void *userobj, void *h_src /* ss handle type */,
1082 		     lws_ss_constate_t state, lws_ss_tx_ordinal_t ack))
1083 {
1084 	if (rx)
1085 		h->ssi.rx = rx;
1086 	if (tx)
1087 		h->ssi.tx = tx;
1088 	if (state)
1089 		h->ssi.state = state;
1090 }
1091 
1092 const char *
lws_sspc_tag(struct lws_sspc_handle * h)1093 lws_sspc_tag(struct lws_sspc_handle *h)
1094 {
1095 	if (!h)
1096 		return "[null sspc]";
1097 	return lws_lc_tag(&h->lc);
1098 }
1099 
1100 int
lws_sspc_cancel_notify_dll(struct lws_dll2 * d,void * user)1101 lws_sspc_cancel_notify_dll(struct lws_dll2 *d, void *user)
1102 {
1103 	lws_sspc_handle_t *h = lws_container_of(d, lws_sspc_handle_t, client_list);
1104 
1105 	lws_sspc_event_helper(h, LWSSSCS_EVENT_WAIT_CANCELLED, 0);
1106 
1107 	return 0;
1108 }
1109 
1110