• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lws-minimal-secure-streams-seq
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 demonstrates the a minimal http client using secure streams api.
11  *
12  * It visits https://warmcat.com/ and receives the html page there.
13  *
14  * This is the "secure streams" api equivalent of minimal-http-client...
15  * it shows how to use a sequencer to make it easy to build more complex
16  * schemes on top of this example.
17  *
18  * The layering looks like this
19  *
20  *                        lifetime
21  *
22  * ------   app   ------  process
23  * ----  sequencer  ----  process
24  * --- secure stream ---  process
25  * -------  wsi  -------  connection
26  *
27  * see minimal-secure-streams for a similar example without the sequencer.
28  */
29 
30 #include <libwebsockets.h>
31 #include <string.h>
32 #include <signal.h>
33 
34 static int interrupted, bad = 1, flag_conn_fail, flag_h1post;
35 static const char * const default_ss_policy =
36 	"{"
37 	  "\"release\":"			"\"01234567\","
38 	  "\"product\":"			"\"myproduct\","
39 	  "\"schema-version\":"			"1,"
40 	  "\"retry\": ["	/* named backoff / retry strategies */
41 		"{\"default\": {"
42 			"\"backoff\": ["	 "1000,"
43 						 "2000,"
44 						 "3000,"
45 						 "5000,"
46 						"10000"
47 				"],"
48 			"\"conceal\":"		"5,"
49 			"\"jitterpc\":"		"20,"
50 			"\"svalidping\":"	"300,"
51 			"\"svalidhup\":"	"310"
52 		"}}"
53 	  "],"
54 	  "\"certs\": [" /* named individual certificates in BASE64 DER */
55 		/*
56 		 * Need to be in order from root cert... notice sometimes as
57 		 * with Let's Encrypt there are multiple possible validation
58 		 * paths, all the pieces for one validation path must be
59 		 * given, excluding the server cert itself.  Let's Encrypt
60 		 * intermediate is signed by their ISRG Root CA but also is
61 		 * cross-signed by an IdenTrust intermediate that's widely
62 		 * deployed in browsers.  We use the ISRG path because that
63 		 * way we can skip the extra IdenTrust root cert.
64 		 */
65 		"{\"isrg_root_x1\": \""
66 	"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw"
67 	"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh"
68 	"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4"
69 	"WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu"
70 	"ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY"
71 	"MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc"
72 	"h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+"
73 	"0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U"
74 	"A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW"
75 	"T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH"
76 	"B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC"
77 	"B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv"
78 	"KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn"
79 	"OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn"
80 	"jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw"
81 	"qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI"
82 	"rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV"
83 	"HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq"
84 	"hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL"
85 	"ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ"
86 	"3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK"
87 	"NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5"
88 	"ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur"
89 	"TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC"
90 	"jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc"
91 	"oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq"
92 	"4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA"
93 	"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d"
94 	"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc="
95 		  "\"}"
96 	  "],"
97 	  "\"trust_stores\": [" /* named cert chains */
98 		"{"
99 			"\"name\": \"le_via_isrg\","
100 			"\"stack\": ["
101 				"\"isrg_root_x1\""
102 			"]"
103 		"}"
104 	  "],"
105 	  "\"s\": [" /* the supported stream types */
106 		"{\"mintest\": {"
107 			"\"endpoint\":"		"\"warmcat.com\","
108 			"\"port\":"		"443,"
109 			"\"protocol\":"		"\"h1\","
110 			"\"http_method\":"	"\"GET\","
111 			"\"http_url\":"		"\"index.html\","
112 			"\"plugins\":"		"[],"
113 			"\"tls\":"		"true,"
114 			"\"opportunistic\":"	"true,"
115 			"\"retry\":"		"\"default\","
116 			"\"tls_trust_store\":"	"\"le_via_isrg\""
117 		"}},"
118 		"{\"mintest-fail\": {"
119 			"\"endpoint\":"		"\"warmcat.com\","
120 			"\"port\":"		"22,"
121 			"\"protocol\":"		"\"h1\","
122 			"\"http_method\":"	"\"GET\","
123 			"\"http_url\":"		"\"index.html\","
124 			"\"plugins\":"		"[],"
125 			"\"tls\":"		"true,"
126 			"\"opportunistic\":"	"true,"
127 			"\"retry\":"		"\"default\","
128 			"\"tls_trust_store\":"	"\"le_via_isrg\""
129 		"}},"
130 		"{\"minpost\": {"
131 			"\"endpoint\":"		"\"warmcat.com\","
132 			"\"port\":"		"443,"
133 			"\"protocol\":"		"\"h1\","
134 			"\"http_method\":"	"\"POST\","
135 			"\"http_url\":"		"\"testserver/formtest\","
136 			"\"plugins\":"		"[],"
137 			"\"tls\":"		"true,"
138 			"\"opportunistic\":"	"true,"
139 			"\"retry\":"		"\"default\","
140 			"\"tls_trust_store\":"	"\"le_via_isrg\""
141 		"}}"
142 	  "]"
143 	"}"
144 ;
145 
146 typedef struct myss {
147 	struct lws_ss_handle 	*ss;
148 	void			*opaque_data;
149 	/* ... application specific state ... */
150 } myss_t;
151 
152 /* secure streams payload interface */
153 
154 static lws_ss_state_return_t
myss_rx(void * userobj,const uint8_t * buf,size_t len,int flags)155 myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
156 {
157 //	myss_t *m = (myss_t *)userobj;
158 
159 	lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
160 	lwsl_hexdump_info(buf, len);
161 
162 	/*
163 	 * If we received the whole message, we let the sequencer know it
164 	 * was a success
165 	 */
166 	if (flags & LWSSS_FLAG_EOM) {
167 		bad = 0;
168 		interrupted = 1;
169 	}
170 
171 	return 0;
172 }
173 
174 static lws_ss_state_return_t
myss_tx(void * userobj,lws_ss_tx_ordinal_t ord,uint8_t * buf,size_t * len,int * flags)175 myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
176 	int *flags)
177 {
178 	// myss_t *m = (myss_t *)userobj;
179 
180 	/* in this example, we don't send any payload */
181 
182 	return 0;
183 }
184 
185 static lws_ss_state_return_t
myss_state(void * userobj,void * sh,lws_ss_constate_t state,lws_ss_tx_ordinal_t ack)186 myss_state(void *userobj, void *sh, lws_ss_constate_t state,
187 		lws_ss_tx_ordinal_t ack)
188 {
189 	myss_t *m = (myss_t *)userobj;
190 
191 	lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
192 		  (unsigned int)ack);
193 
194 	switch (state) {
195 	case LWSSSCS_CREATING:
196 		return lws_ss_request_tx(m->ss);
197 
198 	case LWSSSCS_ALL_RETRIES_FAILED:
199 		/* if we're out of retries, we want to close the app and FAIL */
200 		interrupted = 1;
201 		break;
202 	default:
203 		break;
204 	}
205 
206 	return 0;
207 }
208 
209 typedef enum {
210 	SEQ_IDLE,
211 	SEQ_TRY_CONNECT,
212 	SEQ_RECONNECT_WAIT,
213 	SEQ_CONNECTED,
214 } myseq_state_t;
215 
216 typedef struct myseq {
217 	struct lws_ss_handle	*ss;
218 
219 	myseq_state_t		state;
220 	int			http_resp;
221 
222 	uint16_t		try;
223 } myseq_t;
224 
225 /*
226  * This defines the sequence of things the test app does.
227  */
228 
229 static lws_seq_cb_return_t
min_sec_str_sequencer_cb(struct lws_sequencer * seq,void * user,int event,void * v,void * a)230 min_sec_str_sequencer_cb(struct lws_sequencer *seq, void *user, int event,
231 			 void *v, void *a)
232 {
233 	struct myseq *s = (struct myseq *)user;
234 	lws_ss_info_t ssi;
235 
236 	switch ((int)event) {
237 
238 	/* these messages are created just by virtue of being a sequencer */
239 
240 	case LWSSEQ_CREATED: /* our sequencer just got started */
241 		s->state = SEQ_IDLE;
242 		lwsl_notice("%s: LWSSEQ_CREATED\n", __func__);
243 
244 		/* We're making an outgoing secure stream ourselves */
245 
246 		memset(&ssi, 0, sizeof(ssi));
247 		ssi.handle_offset = offsetof(myss_t, ss);
248 		ssi.opaque_user_data_offset = offsetof(myss_t, opaque_data);
249 		ssi.rx = myss_rx;
250 		ssi.tx = myss_tx;
251 		ssi.state = myss_state;
252 		ssi.user_alloc = sizeof(myss_t);
253 
254 		/* requested to fail (to check backoff)? */
255 		if (flag_conn_fail)
256 			ssi.streamtype = "mintest-fail";
257 		else
258 			/* request to check h1 POST */
259 			if (flag_h1post)
260 				ssi.streamtype = "minpost";
261 			else
262 				/* default to h1 GET */
263 				ssi.streamtype = "mintest";
264 
265 		if (lws_ss_create(lws_seq_get_context(seq), 0, &ssi, NULL,
266 				  &s->ss, seq, NULL)) {
267 			lwsl_err("%s: failed to create secure stream\n",
268 				 __func__);
269 
270 			return LWSSEQ_RET_DESTROY;
271 		}
272 		break;
273 
274 	case LWSSEQ_DESTROYED:
275 		lwsl_notice("%s: LWSSEQ_DESTROYED\n", __func__);
276 		break;
277 
278 	case LWSSEQ_TIMED_OUT: /* current step timed out */
279 		if (s->state == SEQ_RECONNECT_WAIT)
280 			return lws_ss_request_tx(s->ss);
281 		break;
282 
283 	/*
284 	 * These messages are created because we have a secure stream that was
285 	 * bound to this sequencer at creation time.  It copies its state
286 	 * events to us as its sequencer parent.  v is the lws_ss_handle_t *
287 	 */
288 
289 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_CREATING:
290 		lwsl_info("%s: seq LWSSSCS_CREATING\n", __func__);
291 		return lws_ss_request_tx(s->ss);
292 
293 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_DISCONNECTED:
294 		lwsl_info("%s: seq LWSSSCS_DISCONNECTED\n", __func__);
295 		break;
296 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_UNREACHABLE:
297 		lwsl_info("%s: seq LWSSSCS_UNREACHABLE\n", __func__);
298 		break;
299 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_AUTH_FAILED:
300 		lwsl_info("%s: seq LWSSSCS_AUTH_FAILED\n", __func__);
301 		break;
302 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_CONNECTED:
303 		lwsl_info("%s: seq LWSSSCS_CONNECTED\n", __func__);
304 		break;
305 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_CONNECTING:
306 		lwsl_info("%s: seq LWSSSCS_CONNECTING\n", __func__);
307 		break;
308 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_DESTROYING:
309 		lwsl_info("%s: seq LWSSSCS_DESTROYING\n", __func__);
310 		break;
311 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_POLL:
312 		/* somebody called lws_ss_poll() on the stream */
313 		lwsl_info("%s: seq LWSSSCS_POLL\n", __func__);
314 		break;
315 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_ALL_RETRIES_FAILED:
316 		lwsl_info("%s: seq LWSSSCS_ALL_RETRIES_FAILED\n", __func__);
317 		interrupted = 1;
318 		break;
319 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_QOS_ACK_REMOTE:
320 		lwsl_info("%s: seq LWSSSCS_QOS_ACK_REMOTE\n", __func__);
321 		break;
322 	case LWSSEQ_SS_STATE_BASE + LWSSSCS_QOS_ACK_LOCAL:
323 		lwsl_info("%s: seq LWSSSCS_QOS_ACK_LOCAL\n", __func__);
324 		break;
325 
326 	/*
327 	 * This is the message we send from the ss handler to inform the
328 	 * sequencer we had the payload properly
329 	 */
330 
331 	case LWSSEQ_USER_BASE:
332 		bad = 0;
333 		interrupted = 1;
334 		break;
335 
336 	default:
337 		break;
338 	}
339 
340 	return LWSSEQ_RET_CONTINUE;
341 }
342 
343 static void
sigint_handler(int sig)344 sigint_handler(int sig)
345 {
346 	interrupted = 1;
347 }
348 
main(int argc,const char ** argv)349 int main(int argc, const char **argv)
350 {
351 	int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
352 	struct lws_context_creation_info info;
353 	struct lws_context *context;
354 	lws_seq_info_t i;
355 	const char *p;
356 	myseq_t *ms;
357 
358 	signal(SIGINT, sigint_handler);
359 
360 	if ((p = lws_cmdline_option(argc, argv, "-d")))
361 		logs = atoi(p);
362 
363 	lws_set_log_level(logs, NULL);
364 	lwsl_user("LWS minimal secure streams [-d<verbosity>][-f][--h1post]\n");
365 
366 	flag_conn_fail = !!lws_cmdline_option(argc, argv, "-f");
367 	flag_h1post = !!lws_cmdline_option(argc, argv, "--h1post");
368 
369 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
370 
371 	info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
372 		       LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
373 	info.fd_limit_per_thread = 1 + 1 + 1;
374 	info.pss_policies_json = default_ss_policy;
375 	info.port = CONTEXT_PORT_NO_LISTEN;
376 
377 	context = lws_create_context(&info);
378 	if (!context) {
379 		lwsl_err("lws init failed\n");
380 		return 1;
381 	}
382 
383 	/*
384 	 * Create the sequencer that performs the steps of the test action
385 	 * from inside the event loop.
386 	 */
387 
388 	memset(&i, 0, sizeof(i));
389 	i.context	= context;
390 	i.user_size	= sizeof(myseq_t);
391 	i.puser		= (void **)&ms;
392 	i.cb		= min_sec_str_sequencer_cb;
393 	i.name		= "min-sec-stream-seq";
394 
395 	if (!lws_seq_create(&i)) {
396 		lwsl_err("%s: failed to create sequencer\n", __func__);
397 		goto bail;
398 	}
399 
400 	/* the event loop */
401 
402 	while (n >= 0 && !interrupted)
403 		n = lws_service(context, 0);
404 
405 bail:
406 	lws_context_destroy(context);
407 	lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
408 
409 	return bad;
410 }
411