• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2019 - 2020 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  * This is the glue that wires up h1 to Secure Streams.
25  */
26 
27 #include <private-lib-core.h>
28 
29 #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
30 static int
ss_http_multipart_parser(lws_ss_handle_t * h,void * in,size_t len)31 ss_http_multipart_parser(lws_ss_handle_t *h, void *in, size_t len)
32 {
33 	uint8_t *q = (uint8_t *)in;
34 	int pending_issue = 0, n = 0;
35 
36 	/* let's stick it in the boundary state machine first */
37 	while (n < (int)len) {
38 		if (h->u.http.boundary_seq != h->u.http.boundary_len) {
39 			if (q[n] == h->u.http.boundary[h->u.http.boundary_seq])
40 				h->u.http.boundary_seq++;
41 			else {
42 				h->u.http.boundary_seq = 0;
43 				h->u.http.boundary_dashes = 0;
44 				h->u.http.boundary_post = 0;
45 			}
46 			goto around;
47 		}
48 
49 		/*
50 		 * We already matched the boundary string, now we're
51 		 * looking if there's a -- afterwards
52 		 */
53 		if (h->u.http.boundary_dashes < 2) {
54 			if (q[n] == '-') {
55 				h->u.http.boundary_dashes++;
56 				goto around;
57 			}
58 			/* there was no final -- ... */
59 		}
60 
61 		if (h->u.http.boundary_dashes == 2) {
62 			/*
63 			 * It's an EOM boundary: issue pending + multipart EOP
64 			 */
65 			lwsl_debug("%s: seen EOP, n %d pi %d\n",
66 				    __func__, n, pending_issue);
67 			/*
68 			 * It's possible we already started the decode before
69 			 * the end of the last packet.  Then there is no
70 			 * remainder to send.
71 			 */
72 			if (n >= pending_issue + h->u.http.boundary_len +
73 			    (h->u.http.any ? 2 : 0) + 1)
74 				h->info.rx(ss_to_userobj(h),
75 					   &q[pending_issue],
76 					   n - pending_issue -
77 					   h->u.http.boundary_len - 1 -
78 					   (h->u.http.any ? 2 : 0) /* crlf */,
79 				   (!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
80 				   LWSSS_FLAG_EOM | LWSSS_FLAG_RELATED_END);
81 
82 			/*
83 			 * Peer may not END_STREAM us
84 			 */
85 			return 0;
86 			//return -1;
87 		}
88 
89 		/* how about --boundaryCRLF */
90 
91 		if (h->u.http.boundary_post < 2) {
92 			if ((!h->u.http.boundary_post && q[n] == '\x0d') ||
93 			    (h->u.http.boundary_post && q[n] == '\x0a')) {
94 				h->u.http.boundary_post++;
95 				goto around;
96 			}
97 			/* there was no final CRLF ... it's wrong */
98 
99 			return -1;
100 		}
101 		if (h->u.http.boundary_post != 2)
102 			goto around;
103 
104 		/*
105 		 * We have a starting "--boundaryCRLF" or intermediate
106 		 * "CRLF--boundaryCRLF" boundary
107 		 */
108 		lwsl_debug("%s: b_post = 2 (pi %d)\n", __func__, pending_issue);
109 		h->u.http.boundary_seq = 0;
110 		h->u.http.boundary_post = 0;
111 
112 		if (n >= pending_issue && (h->u.http.any || !h->u.http.som)) {
113 			/* Intermediate... do the EOM */
114 			lwsl_debug("%s: seen interm EOP n %d pi %d\n", __func__,
115 				   n, pending_issue);
116 			/*
117 			 * It's possible we already started the decode before
118 			 * the end of the last packet.  Then there is no
119 			 * remainder to send.
120 			 */
121 			if (n >= pending_issue + h->u.http.boundary_len +
122 			    (h->u.http.any ? 2 : 0))
123 				h->info.rx(ss_to_userobj(h), &q[pending_issue],
124 					   n - pending_issue -
125 					       h->u.http.boundary_len -
126 					       (h->u.http.any ? 2 /* crlf */ : 0),
127 					   (!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
128 					   LWSSS_FLAG_EOM);
129 		}
130 
131 		/* Next message starts after this boundary */
132 
133 		pending_issue = n;
134 		h->u.http.som = 0;
135 
136 around:
137 		n++;
138 	}
139 
140 	if (pending_issue != n) {
141 		h->info.rx(ss_to_userobj(h), &q[pending_issue], n - pending_issue,
142 			   (!h->u.http.som ? LWSSS_FLAG_SOM : 0));
143 		h->u.http.any = 1;
144 		h->u.http.som = 1;
145 	}
146 
147 	return 0;
148 }
149 #endif
150 
151 static const uint8_t blob_idx[] = {
152 	LWS_SYSBLOB_TYPE_AUTH,
153 	LWS_SYSBLOB_TYPE_DEVICE_SERIAL,
154 	LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION,
155 	LWS_SYSBLOB_TYPE_DEVICE_TYPE,
156 };
157 
158 int
secstream_h1(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)159 secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
160 	     void *in, size_t len)
161 {
162 	lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
163 	uint8_t buf[LWS_PRE + 1520], *p = &buf[LWS_PRE],
164 		*end = &buf[sizeof(buf) - 1];
165 	int f = 0, m, status;
166 	size_t buflen;
167 
168 	switch (reason) {
169 
170 	/* because we are protocols[0] ... */
171 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
172 		assert(h);
173 		assert(h->policy);
174 		lwsl_info("%s: h: %p, %s CLIENT_CONNECTION_ERROR: %s\n", __func__,
175 			  h, h->policy->streamtype, in ? (char *)in : "(null)");
176 		lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
177 		h->wsi = NULL;
178 		lws_ss_backoff(h);
179 		break;
180 
181 	case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
182 		if (!h)
183 			break;
184 		lwsl_info("%s: h: %p, %s LWS_CALLBACK_CLOSED_CLIENT_HTTP\n",
185 			  __func__, h,
186 			  h->policy ? h->policy->streamtype : "no policy");
187 		h->wsi = NULL;
188 		//bad = status != 200;
189 		//lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
190 		if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) &&
191 		    !h->txn_ok && !wsi->context->being_destroyed)
192 			lws_ss_backoff(h);
193 		if (lws_ss_event_helper(h, LWSSSCS_DISCONNECTED))
194 			lws_ss_destroy(&h);
195 		break;
196 
197 
198 	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
199 		status = lws_http_client_http_response(wsi);
200 		lwsl_info("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: %d\n", __func__, status);
201 	//	if (!status)
202 			/* it's just telling use we connected / joined the nwsi */
203 	//		break;
204 		h->u.http.good_respcode = (status >= 200 && status < 300);
205 		// lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode);
206 
207 		if (h->u.http.good_respcode)
208 			lwsl_info("%s: Connected streamtype %s, %d\n", __func__,
209 				  h->policy->streamtype, status);
210 		else
211 			lwsl_warn("%s: Connected streamtype %s, BAD %d\n", __func__,
212 				  h->policy->streamtype, status);
213 
214 		h->hanging_som = 0;
215 
216 		h->retry = 0;
217 		h->seqstate = SSSEQ_CONNECTED;
218 		lws_ss_set_timeout_us(h, LWS_SET_TIMER_USEC_CANCEL);
219 		lws_ss_event_helper(h, LWSSSCS_CONNECTED);
220 
221 		/*
222 		 * Since it's an http transaction we initiated... this is
223 		 * proof of connection validity
224 		 */
225 		lws_validity_confirmed(wsi);
226 
227 #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
228 
229 		if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf),
230 				 WSI_TOKEN_HTTP_CONTENT_TYPE) > 0 &&
231 		/* multipart/form-data;
232 		 * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
233 
234 		    (!strncmp((char *)buf, "multipart/form-data", 19) ||
235 		     !strncmp((char *)buf, "multipart/related", 17))) {
236 			struct lws_tokenize ts;
237 			lws_tokenize_elem e;
238 
239 			// puts((const char *)buf);
240 
241 			memset(&ts, 0, sizeof(ts));
242 			ts.start = (char *)buf;
243 			ts.len = strlen(ts.start);
244 			ts.flags = LWS_TOKENIZE_F_RFC7230_DELIMS |
245 					LWS_TOKENIZE_F_SLASH_NONTERM |
246 					LWS_TOKENIZE_F_MINUS_NONTERM;
247 
248 			h->u.http.boundary[0] = '\0';
249 			do {
250 				e = lws_tokenize(&ts);
251 				if (e == LWS_TOKZE_TOKEN_NAME_EQUALS &&
252 				    !strncmp(ts.token, "boundary", 8) &&
253 				    ts.token_len == 8) {
254 					e = lws_tokenize(&ts);
255 					if (e != LWS_TOKZE_TOKEN)
256 						goto malformed;
257 					h->u.http.boundary[0] = '\x0d';
258 					h->u.http.boundary[1] = '\x0a';
259 					h->u.http.boundary[2] = '-';
260 					h->u.http.boundary[3] = '-';
261 					lws_strnncpy(h->u.http.boundary + 4,
262 						     ts.token, ts.token_len,
263 						     sizeof(h->u.http.boundary) - 4);
264 					h->u.http.boundary_len = ts.token_len + 4;
265 					h->u.http.boundary_seq = 2;
266 					h->u.http.boundary_dashes = 0;
267 				}
268 			} while (e > 0);
269 			lwsl_info("%s: multipart boundary '%s' len %d\n", __func__,
270 					h->u.http.boundary, h->u.http.boundary_len);
271 
272 			/* inform the ss that a related message group begins */
273 
274 			if (h->u.http.boundary[0])
275 				h->info.rx(ss_to_userobj(h), NULL, 0,
276 					   LWSSS_FLAG_RELATED_START);
277 
278 			// lws_header_table_detach(wsi, 0);
279 		}
280 		break;
281 malformed:
282 		lwsl_notice("%s: malformed multipart header\n", __func__);
283 		return -1;
284 #else
285 		break;
286 #endif
287 
288 	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
289 		if (h->writeable_len)
290 			wsi->http.writeable_len = h->writeable_len;
291 
292 		{
293 			uint8_t **p = (uint8_t **)in, *end = (*p) + len,
294 				*oin = *(uint8_t **)in;
295 
296 		/*
297 		 * blob-based headers
298 		 */
299 
300 		for (m = 0; m < _LWSSS_HBI_COUNT; m++) {
301 			int o = 0, n;
302 
303 			if (!h->policy->u.http.blob_header[m])
304 				continue;
305 
306 			if (m == LWSSS_HBI_AUTH &&
307 			    h->policy->u.http.auth_preamble)
308 				o = lws_snprintf((char *)buf, sizeof(buf), "%s",
309 					h->policy->u.http.auth_preamble);
310 
311 			if (o > (int)sizeof(buf) - 2)
312 				return -1;
313 
314 			buflen = sizeof(buf) - o - 2;
315 			n = lws_system_blob_get(
316 				lws_system_get_blob(wsi->context, blob_idx[m], 0),
317 						    buf + o, &buflen, 0);
318 			if (n < 0)
319 				return -1;
320 
321 			buf[o + buflen] = '\0';
322 			lwsl_debug("%s: adding blob %d: %s\n", __func__, m, buf);
323 
324 			if (lws_add_http_header_by_name(wsi,
325 					 (uint8_t *)h->policy->u.http.blob_header[m],
326 					 buf, buflen + o, p, end))
327 				return -1;
328 		}
329 
330 		/*
331 		 * metadata-based headers
332 		 */
333 
334 		for (m = 0; m < h->policy->metadata_count; m++) {
335 			lws_ss_metadata_t *polmd;
336 
337 			/* has to have a header string listed */
338 			if (!h->metadata[m].value)
339 				continue;
340 
341 			polmd = lws_ss_policy_metadata_index(h->policy, m);
342 
343 			assert(polmd);
344 			/* has to have a value */
345 			if (polmd->value && ((uint8_t *)polmd->value)[0]) {
346 				if (lws_add_http_header_by_name(wsi,
347 						polmd->value,
348 						h->metadata[m].value,
349 						h->metadata[m].length, p, end))
350 				return -1;
351 			}
352 		}
353 
354 		/*
355 		 * Content-length on POST if we have the length information
356 		 */
357 
358 		if (!strcmp(h->policy->u.http.method, "POST") &&
359 		    wsi->http.writeable_len) {
360 			if (!(h->policy->flags &
361 				LWSSSPOLF_HTTP_NO_CONTENT_LENGTH)) {
362 				int n = lws_snprintf((char *)buf, 20, "%u",
363 					(unsigned int)wsi->http.writeable_len);
364 				if (lws_add_http_header_by_token(wsi,
365 						WSI_TOKEN_HTTP_CONTENT_LENGTH,
366 						buf, n, p, end))
367 					return -1;
368 			}
369 			lws_client_http_body_pending(wsi, 1);
370 		}
371 
372 		(void)oin;
373 		// if (*p != oin)
374 		//	lwsl_hexdump_notice(oin, lws_ptr_diff(*p, oin));
375 
376 		}
377 
378 		break;
379 
380 	/* chunks of chunked content, with header removed */
381 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
382 		lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ: read %d\n",
383 				__func__, (int)len);
384 		if (!h)
385 			return 0;
386 
387 #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
388 		if (h->u.http.boundary[0])
389 			return ss_http_multipart_parser(h, in, len);
390 #endif
391 
392 		if (!h->subseq) {
393 			f |= LWSSS_FLAG_SOM;
394 			h->hanging_som = 1;
395 			h->subseq = 1;
396 		}
397 
398 	//	lwsl_notice("%s: HTTP_READ: client side sent len %d fl 0x%x\n",
399 	//		    __func__, (int)len, (int)f);
400 
401 		h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f);
402 
403 		return 0; /* don't passthru */
404 
405 	/* uninterpreted http content */
406 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
407 		{
408 			char *px = (char *)buf + LWS_PRE; /* guarantees LWS_PRE */
409 			int lenx = sizeof(buf) - LWS_PRE;
410 
411 			if (lws_http_client_read(wsi, &px, &lenx) < 0)
412 				return -1;
413 		}
414 		lws_set_timeout(wsi, 99, 30);
415 
416 		return 0; /* don't passthru */
417 
418 	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
419 		lwsl_debug("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n", __func__);
420 		if (h->hanging_som)
421 			h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM);
422 
423 		wsi->http.writeable_len = h->writeable_len = 0;
424 
425 		if (h->u.http.good_respcode)
426 			lws_ss_event_helper(h, LWSSSCS_QOS_ACK_REMOTE);
427 		else
428 			lws_ss_event_helper(h, LWSSSCS_QOS_NACK_REMOTE);
429 
430 		h->wsi = NULL;
431 		h->txn_ok = 1;
432 		//bad = status != 200;
433 		lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
434 		break;
435 
436 	case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
437 		lwsl_info("%s: LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n", __func__);
438 		if (!h)
439 			return 0;
440 
441 		if (!h->rideshare)
442 			h->rideshare = h->policy;
443 
444 #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
445 		if (!h->inside_msg && h->rideshare->u.http.multipart_name)
446 			lws_client_http_multipart(wsi,
447 				h->rideshare->u.http.multipart_name,
448 				h->rideshare->u.http.multipart_filename,
449 				h->rideshare->u.http.multipart_content_type,
450 				(char **)&p, (char *)end);
451 
452 		buflen = lws_ptr_diff(end, p);
453 		if (h->policy->u.http.multipart_name)
454 			buflen -= 24; /* allow space for end of multipart */
455 
456 #endif
457 
458 		if (h->info.tx(ss_to_userobj(h),  h->txord++, p, &buflen, &f)) {
459 			/* don't want to send anything */
460 			lwsl_debug("%s: dont want to write\n", __func__);
461 			return 0;
462 		}
463 
464 		lwsl_info("%s: WRITEABLE: user tx says len %d fl 0x%x\n",
465 			    __func__, (int)buflen, (int)f);
466 
467 		p += buflen;
468 
469 		if (f & LWSSS_FLAG_EOM) {
470 #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
471 			/* end of rideshares */
472 			if (!h->rideshare->rideshare_streamtype) {
473 				lws_client_http_body_pending(wsi, 0);
474 				if (h->rideshare->u.http.multipart_name)
475 					lws_client_http_multipart(wsi, NULL, NULL, NULL,
476 						(char **)&p, (char *)end);
477 			} else {
478 #endif
479 				h->rideshare = lws_ss_policy_lookup(wsi->context,
480 						h->rideshare->rideshare_streamtype);
481 				lws_callback_on_writable(wsi);
482 #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
483 			}
484 #endif
485 
486 			h->inside_msg = 0;
487 		} else {
488 			/* otherwise we can spin with zero length writes */
489 			if (!f && !lws_ptr_diff(p, buf + LWS_PRE))
490 				break;
491 			h->inside_msg = 1;
492 			lws_callback_on_writable(wsi);
493 		}
494 
495 		lwsl_info("%s: lws_write %d %d\n", __func__,
496 			  lws_ptr_diff(p, buf + LWS_PRE), f);
497 
498 		if (lws_write(wsi, buf + LWS_PRE, lws_ptr_diff(p, buf + LWS_PRE),
499 			 LWS_WRITE_HTTP) != (int)lws_ptr_diff(p, buf + LWS_PRE)) {
500 			lwsl_err("%s: write failed\n", __func__);
501 			return -1;
502 		}
503 
504 		lws_set_timeout(wsi, 0, 0);
505 		break;
506 
507 	default:
508 		break;
509 	}
510 
511 	return lws_callback_http_dummy(wsi, reason, user, in, len);
512 }
513 
514 const struct lws_protocols protocol_secstream_h1 = {
515 	"lws-secstream-h1",
516 	secstream_h1,
517 	0,
518 	0,
519 };
520 
521 /*
522  * Munge connect info according to protocol-specific considerations... this
523  * usually means interpreting aux in a protocol-specific way and using the
524  * pieces at connection setup time, eg, http url pieces.
525  *
526  * len bytes of buf can be used for things with scope until after the actual
527  * connect.
528  */
529 
530 static int
secstream_connect_munge_h1(lws_ss_handle_t * h,char * buf,size_t len,struct lws_client_connect_info * i,union lws_ss_contemp * ct)531 secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len,
532 			   struct lws_client_connect_info *i,
533 			   union lws_ss_contemp *ct)
534 {
535 	size_t used_in, used_out;
536 	lws_strexp_t exp;
537 
538 	if (!h->policy->u.http.url)
539 		return 0;
540 
541 #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
542 	if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART)
543 		i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME;
544 
545 	if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED)
546 		i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED;
547 #endif
548 
549 	/* protocol aux is the path part */
550 
551 	i->path = buf;
552 	buf[0] = '/';
553 
554 	lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1);
555 
556 	if (lws_strexp_expand(&exp, h->policy->u.http.url,
557 			      strlen(h->policy->u.http.url),
558 			      &used_in, &used_out) != LSTRX_DONE)
559 		return 1;
560 
561 	return 0;
562 }
563 
564 
565 const struct ss_pcols ss_pcol_h1 = {
566 	"h1",
567 	"http/1.1",
568 	"lws-secstream-h1",
569 	secstream_connect_munge_h1,
570 	NULL
571 };
572