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 #define LWS_WITH_SS_RIDESHARE
31 #endif
32
33 #if defined(LWS_WITH_SS_RIDESHARE)
34 static int
ss_http_multipart_parser(lws_ss_handle_t * h,void * in,size_t len)35 ss_http_multipart_parser(lws_ss_handle_t *h, void *in, size_t len)
36 {
37 uint8_t *q = (uint8_t *)in;
38 int pending_issue = 0, n = 0;
39
40
41 /* let's stick it in the boundary state machine first */
42 while (n < (int)len) {
43 if (h->u.http.boundary_seq != h->u.http.boundary_len) {
44 if (q[n] == h->u.http.boundary[h->u.http.boundary_seq])
45 h->u.http.boundary_seq++;
46 else {
47 h->u.http.boundary_seq = 0;
48 h->u.http.boundary_dashes = 0;
49 h->u.http.boundary_post = 0;
50 }
51 goto around;
52 }
53
54 /*
55 * We already matched the boundary string, now we're
56 * looking if there's a -- afterwards
57 */
58 if (h->u.http.boundary_dashes < 2) {
59 if (q[n] == '-') {
60 h->u.http.boundary_dashes++;
61 goto around;
62 }
63 /* there was no final -- ... */
64 }
65
66 if (h->u.http.boundary_dashes == 2) {
67 /*
68 * It's an EOM boundary: issue pending + multipart EOP
69 */
70 lwsl_debug("%s: seen EOP, n %d pi %d\n",
71 __func__, n, pending_issue);
72 /*
73 * It's possible we already started the decode before
74 * the end of the last packet. Then there is no
75 * remainder to send.
76 */
77 if (n >= pending_issue + h->u.http.boundary_len +
78 (h->u.http.any ? 2 : 0) + 1) {
79 h->info.rx(ss_to_userobj(h),
80 &q[pending_issue],
81 (unsigned int)(n - pending_issue -
82 h->u.http.boundary_len - 1 -
83 (h->u.http.any ? 2 : 0) /* crlf */),
84 (!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
85 LWSSS_FLAG_EOM | LWSSS_FLAG_RELATED_END);
86 h->u.http.eom = 1;
87 }
88
89 /*
90 * Peer may not END_STREAM us
91 */
92 return 0;
93 //return -1;
94 }
95
96 /* how about --boundaryCRLF */
97
98 if (h->u.http.boundary_post < 2) {
99 if ((!h->u.http.boundary_post && q[n] == '\x0d') ||
100 (h->u.http.boundary_post && q[n] == '\x0a')) {
101 h->u.http.boundary_post++;
102 goto around;
103 }
104 /* there was no final CRLF ... it's wrong */
105
106 return -1;
107 }
108 if (h->u.http.boundary_post != 2)
109 goto around;
110
111 /*
112 * We have a starting "--boundaryCRLF" or intermediate
113 * "CRLF--boundaryCRLF" boundary
114 */
115 lwsl_debug("%s: b_post = 2 (pi %d)\n", __func__, pending_issue);
116 h->u.http.boundary_seq = 0;
117 h->u.http.boundary_post = 0;
118
119 if (n >= pending_issue && (h->u.http.any || !h->u.http.som)) {
120 /* Intermediate... do the EOM */
121 lwsl_debug("%s: seen interm EOP n %d pi %d\n", __func__,
122 n, pending_issue);
123 /*
124 * It's possible we already started the decode before
125 * the end of the last packet. Then there is no
126 * remainder to send.
127 */
128 if (n >= pending_issue + h->u.http.boundary_len +
129 (h->u.http.any ? 2 : 0)) {
130 h->info.rx(ss_to_userobj(h), &q[pending_issue],
131 (unsigned int)(n - pending_issue -
132 h->u.http.boundary_len -
133 (h->u.http.any ? 2 /* crlf */ : 0)),
134 (!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
135 LWSSS_FLAG_EOM);
136 h->u.http.eom = 1;
137 }
138 }
139
140 /* Next message starts after this boundary */
141
142 pending_issue = n;
143 if (h->u.http.eom) {
144 /* reset only if we have sent eom */
145 h->u.http.som = 0;
146 h->u.http.eom = 0;
147 }
148
149 around:
150 n++;
151 }
152
153 if (pending_issue != n) {
154 uint8_t oh = 0;
155
156 /*
157 * handle the first or last "--boundaryCRLF" case which is not captured in the
158 * previous loop, on the Bob downchannel (/directive)
159 *
160 * probably does not cover the case that one boundary term is separated in multipile
161 * one callbacks though never see such case
162 */
163
164 if ((n >= h->u.http.boundary_len) &&
165 h->u.http.boundary_seq == h->u.http.boundary_len &&
166 h->u.http.boundary_post == 2) {
167
168 oh = 1;
169 }
170
171 h->info.rx(ss_to_userobj(h), &q[pending_issue],
172 (unsigned int)(oh ?
173 (n - pending_issue - h->u.http.boundary_len -
174 (h->u.http.any ? 2 : 0)) :
175 (n - pending_issue)),
176 (!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
177 (oh && h->u.http.any ? LWSSS_FLAG_EOM : 0));
178
179 if (oh && h->u.http.any)
180 h->u.http.eom = 1;
181
182 h->u.http.any = 1;
183 h->u.http.som = 1;
184 }
185
186 return 0;
187 }
188 #endif
189
190 /*
191 * Returns 0, or the ss state resp maps on to
192 */
193
194 static int
lws_ss_http_resp_to_state(lws_ss_handle_t * h,int resp)195 lws_ss_http_resp_to_state(lws_ss_handle_t *h, int resp)
196 {
197 const lws_ss_http_respmap_t *r = h->policy->u.http.respmap;
198 int n = h->policy->u.http.count_respmap;
199
200 while (n--)
201 if (resp == r->resp)
202 return r->state;
203 else
204 r++;
205
206 return 0; /* no hit */
207 }
208
209 /*
210 * This converts any set metadata items into outgoing http headers
211 */
212
213 static int
lws_apply_metadata(lws_ss_handle_t * h,struct lws * wsi,uint8_t * buf,uint8_t ** pp,uint8_t * end)214 lws_apply_metadata(lws_ss_handle_t *h, struct lws *wsi, uint8_t *buf,
215 uint8_t **pp, uint8_t *end)
216 {
217 lws_ss_metadata_t *polmd = h->policy->metadata;
218 int m = 0;
219
220 while (polmd) {
221
222 /* has to have a non-empty header string */
223
224 if (polmd->value__may_own_heap &&
225 ((uint8_t *)polmd->value__may_own_heap)[0] &&
226 h->metadata[m].value__may_own_heap) {
227 if (lws_add_http_header_by_name(wsi,
228 polmd->value__may_own_heap,
229 h->metadata[m].value__may_own_heap,
230 (int)h->metadata[m].length, pp, end))
231 return -1;
232
233 /*
234 * Check for the case he's setting a non-zero
235 * content-length "via the backdoor" metadata-
236 * driven headers, and set the body_pending()
237 * state if so...
238 */
239
240 if (!strncmp(polmd->value__may_own_heap,
241 "content-length", 14) &&
242 atoi(h->metadata[m].value__may_own_heap))
243 lws_client_http_body_pending(wsi, 1);
244 }
245
246 m++;
247 polmd = polmd->next;
248 }
249
250 /*
251 * Content-length on POST / PUT / PATCH if we have the length information
252 */
253
254 if (h->policy->u.http.method && (
255 (!strcmp(h->policy->u.http.method, "POST") ||
256 !strcmp(h->policy->u.http.method, "PATCH") ||
257 !strcmp(h->policy->u.http.method, "PUT"))) &&
258 wsi->http.writeable_len) {
259 if (!(h->policy->flags &
260 LWSSSPOLF_HTTP_NO_CONTENT_LENGTH)) {
261 int n = lws_snprintf((char *)buf, 20, "%u",
262 (unsigned int)wsi->http.writeable_len);
263 if (lws_add_http_header_by_token(wsi,
264 WSI_TOKEN_HTTP_CONTENT_LENGTH,
265 buf, n, pp, end))
266 return -1;
267 }
268 lws_client_http_body_pending(wsi, 1);
269 }
270
271 return 0;
272 }
273
274
275 #if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
276 static int
lws_apply_instant_metadata(lws_ss_handle_t * h,struct lws * wsi,uint8_t * buf,uint8_t ** pp,uint8_t * end)277 lws_apply_instant_metadata(lws_ss_handle_t *h, struct lws *wsi, uint8_t *buf,
278 uint8_t **pp, uint8_t *end)
279 {
280 lws_ss_metadata_t *imd = h->instant_metadata;
281
282 while (imd) {
283 if (imd->name && imd->value__may_own_heap) {
284 lwsl_debug("%s add header %s %s %d\n", __func__,
285 imd->name,
286 (char *)imd->value__may_own_heap,
287 (int)imd->length);
288 if (lws_add_http_header_by_name(wsi,
289 (const unsigned char *)imd->name,
290 (const unsigned char *)imd->value__may_own_heap,
291 (int)imd->length, pp, end))
292 return -1;
293
294 /* it's possible user set content-length directly */
295 if (!strncmp(imd->name,
296 "content-length", 14) &&
297 atoi(imd->value__may_own_heap))
298 lws_client_http_body_pending(wsi, 1);
299
300 }
301
302 imd = imd->next;
303 }
304
305 return 0;
306 }
307 #endif
308 /*
309 * Check if any metadata headers present in the server headers, and record
310 * them into the associated metadata item if so.
311 */
312
313 static int
lws_extract_metadata(lws_ss_handle_t * h,struct lws * wsi)314 lws_extract_metadata(lws_ss_handle_t *h, struct lws *wsi)
315 {
316 lws_ss_metadata_t *polmd = h->policy->metadata, *omd;
317 int n;
318
319 while (polmd) {
320
321 if (polmd->value_is_http_token != LWS_HTTP_NO_KNOWN_HEADER) {
322
323 /* it's a well-known header token */
324
325 n = lws_hdr_total_length(wsi, polmd->value_is_http_token);
326 if (n) {
327 const char *cp = lws_hdr_simple_ptr(wsi,
328 polmd->value_is_http_token);
329 omd = lws_ss_get_handle_metadata(h, polmd->name);
330 if (!omd || !cp)
331 return 1;
332
333 assert(!strcmp(omd->name, polmd->name));
334
335 /*
336 * it's present on the wsi, we want to
337 * set the related metadata name to it then
338 */
339
340 _lws_ss_alloc_set_metadata(omd, polmd->name, cp,
341 (unsigned int)n);
342
343 #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
344 /*
345 * ...and because we are doing it from parsing
346 * onward rx, we want to mark the metadata as
347 * needing passing to the client
348 */
349 omd->pending_onward = 1;
350 #endif
351 }
352 }
353
354 #if defined(LWS_WITH_CUSTOM_HEADERS)
355 else
356
357 /* has to have a non-empty header string */
358
359 if (polmd->value__may_own_heap &&
360 ((uint8_t *)polmd->value__may_own_heap)[0]) {
361 char *p;
362
363 /*
364 * Can it be a custom header?
365 */
366
367 n = lws_hdr_custom_length(wsi, (const char *)
368 polmd->value__may_own_heap,
369 polmd->value_length);
370 if (n > 0) {
371
372 p = lws_malloc((unsigned int)n + 1, __func__);
373 if (!p)
374 return 1;
375
376 /* if needed, free any previous value */
377
378 if (polmd->value_on_lws_heap) {
379 lws_free(
380 polmd->value__may_own_heap);
381 polmd->value_on_lws_heap = 0;
382 }
383
384 /*
385 * copy the named custom header value
386 * into the malloc'd buffer
387 */
388
389 if (lws_hdr_custom_copy(wsi, p, n + 1,
390 (const char *)
391 polmd->value__may_own_heap,
392 polmd->value_length) < 0) {
393 lws_free(p);
394
395 return 1;
396 }
397
398 omd = lws_ss_get_handle_metadata(h,
399 polmd->name);
400 if (omd) {
401
402 _lws_ss_set_metadata(omd,
403 polmd->name, p, (size_t)n);
404 omd->value_on_lws_heap = 1;
405
406 #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
407 omd->pending_onward = 1;
408 #endif
409 }
410 }
411 }
412 #endif
413
414 polmd = polmd->next;
415 }
416
417 return 0;
418 }
419
420 static const uint8_t blob_idx[] = {
421 LWS_SYSBLOB_TYPE_AUTH,
422 LWS_SYSBLOB_TYPE_DEVICE_SERIAL,
423 LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION,
424 LWS_SYSBLOB_TYPE_DEVICE_TYPE,
425 };
426
427 int
secstream_h1(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)428 secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
429 void *in, size_t len)
430 {
431 #if defined(LWS_WITH_SERVER)
432 struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
433 #endif
434 lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
435 uint8_t buf[LWS_PRE + 1520], *p = &buf[LWS_PRE],
436 #if defined(LWS_WITH_SERVER)
437 *start = p,
438 #endif
439 *end = &buf[sizeof(buf) - 1];
440 lws_ss_state_return_t r;
441 int f = 0, m, status;
442 char conceal_eom = 0;
443 lws_usec_t inter;
444 size_t buflen;
445
446 switch (reason) {
447
448 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
449 if (!h) {
450 lwsl_err("%s: CCE with no ss handle %s\n", __func__, lws_wsi_tag(wsi));
451 break;
452 }
453
454 lws_ss_assert_extant(wsi->a.context, wsi->tsi, h);
455
456 assert(h->policy);
457
458 #if defined(LWS_WITH_CONMON)
459 lws_conmon_ss_json(h);
460 #endif
461
462 lws_metrics_caliper_report_hist(h->cal_txn, wsi);
463 lwsl_info("%s: %s CLIENT_CONNECTION_ERROR: %s\n", __func__,
464 h->lc.gutag, in ? (const char *)in : "none");
465 if (h->ss_dangling_connected) {
466 /* already disconnected, no action for DISCONNECT_ME */
467 r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED);
468 if (r != LWSSSSRET_OK)
469 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
470 } else {
471 /* already disconnected, no action for DISCONNECT_ME */
472 r = lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
473 if (r) {
474 if (h->inside_connect) {
475 h->pending_ret = r;
476 break;
477 }
478
479 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
480 }
481 }
482
483 h->wsi = NULL;
484 r = lws_ss_backoff(h);
485 if (r != LWSSSSRET_OK) {
486 if (h->inside_connect) {
487 h->pending_ret = r;
488 break;
489 }
490 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
491 }
492 break;
493
494 case LWS_CALLBACK_CLIENT_HTTP_REDIRECT:
495
496 if (!h)
497 return -1;
498
499 if (h->policy->u.http.fail_redirect)
500 lws_system_cpd_set(lws_get_context(wsi),
501 LWS_CPD_CAPTIVE_PORTAL);
502 /* unless it's explicitly allowed, reject to follow it */
503 return !(h->policy->flags & LWSSSPOLF_ALLOW_REDIRECTS);
504
505 case LWS_CALLBACK_CLOSED_HTTP: /* server */
506 case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
507 if (!h)
508 break;
509
510 lws_sul_cancel(&h->sul_timeout);
511
512 lws_ss_assert_extant(wsi->a.context, wsi->tsi, h);
513
514 #if defined(LWS_WITH_CONMON)
515 if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) {
516 wsi->conmon.pcol = LWSCONMON_PCOL_HTTP;
517 wsi->conmon.protocol_specific.http.response =
518 (int)lws_http_client_http_response(wsi);
519 }
520
521 lws_conmon_ss_json(h);
522 #endif
523
524 lws_metrics_caliper_report_hist(h->cal_txn, wsi);
525 //lwsl_notice("%s: %s LWS_CALLBACK_CLOSED_CLIENT_HTTP\n",
526 // __func__, wsi->lc.gutag);
527
528 h->wsi = NULL;
529 h->hanging_som = 0;
530 h->subseq = 0;
531
532 #if defined(LWS_WITH_SERVER)
533 lws_pt_lock(pt, __func__);
534 lws_dll2_remove(&h->cli_list);
535 lws_pt_unlock(pt);
536 #endif
537
538 if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) &&
539 #if defined(LWS_WITH_SERVER)
540 !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not server */
541 #endif
542 !h->txn_ok && !wsi->a.context->being_destroyed) {
543 r = lws_ss_backoff(h);
544 if (r != LWSSSSRET_OK)
545 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
546 break;
547 } else
548 h->seqstate = SSSEQ_IDLE;
549
550 if (h->ss_dangling_connected) {
551 /* already disconnected, no action for DISCONNECT_ME */
552 r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED);
553 if (r != LWSSSSRET_OK)
554 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
555 }
556 break;
557
558 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
559
560 if (!h)
561 return -1;
562
563 lws_ss_assert_extant(wsi->a.context, wsi->tsi, h);
564 h->wsi = wsi; /* since we accept the wsi is bound to the SS,
565 * ensure the SS feels the same way about the wsi */
566
567 #if defined(LWS_WITH_CONMON)
568 if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) {
569 wsi->conmon.pcol = LWSCONMON_PCOL_HTTP;
570 wsi->conmon.protocol_specific.http.response =
571 (int)lws_http_client_http_response(wsi);
572 }
573
574 lws_conmon_ss_json(h);
575 #endif
576
577 status = (int)lws_http_client_http_response(wsi);
578 lwsl_info("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: %d\n", __func__, status);
579 // if (!status)
580 /* it's just telling use we connected / joined the nwsi */
581 // break;
582
583 #if defined(LWS_WITH_SYS_METRICS)
584 if (status) {
585 lws_snprintf((char *)buf, 10, "%d", status);
586 lws_metrics_tag_ss_add(h, "http_resp", (char *)buf);
587 }
588 #endif
589
590 if (status == HTTP_STATUS_SERVICE_UNAVAILABLE /* 503 */ ||
591 status == 429 /* Too many requests */) {
592 /*
593 * We understand this attempt failed, and that we should
594 * conceal this attempt. If there's a specified
595 * retry-after, we should use that if larger than our
596 * computed backoff
597 */
598
599 inter = 0;
600 lws_http_check_retry_after(wsi, &inter);
601
602 r = _lws_ss_backoff(h, inter);
603 if (r != LWSSSSRET_OK)
604 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
605
606 return -1; /* end this stream */
607 }
608
609 if (h->policy->u.http.resp_expect)
610 h->u.http.good_respcode =
611 status == h->policy->u.http.resp_expect;
612 else
613 h->u.http.good_respcode = (status >= 200 && status < 300);
614 // lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode);
615
616 if (lws_extract_metadata(h, wsi)) {
617 lwsl_info("%s: rx metadata extract failed\n", __func__);
618
619 return -1;
620 }
621
622 if (status) {
623 /*
624 * Check and see if it's something from the response
625 * map, if so, generate the requested status. If we're
626 * the proxy onward connection, metadata has priority
627 * over state updates on the serialization, so the
628 * state callback will see the right metadata.
629 */
630 int n = lws_ss_http_resp_to_state(h, status);
631 if (n) {
632 r = lws_ss_event_helper(h, (lws_ss_constate_t)n);
633 if (r != LWSSSSRET_OK)
634 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi,
635 &h);
636 }
637 }
638
639 if (h->u.http.good_respcode)
640 lwsl_info("%s: Connected streamtype %s, %d\n", __func__,
641 h->policy->streamtype, status);
642 else
643 if (h->u.http.good_respcode)
644 lwsl_warn("%s: Connected streamtype %s, BAD %d\n",
645 __func__, h->policy->streamtype,
646 status);
647
648 h->hanging_som = 0;
649
650 h->retry = 0;
651 h->seqstate = SSSEQ_CONNECTED;
652 lws_sul_cancel(&h->sul);
653
654 if (h->prev_ss_state != LWSSSCS_CONNECTED) {
655 wsi->client_suppress_CONNECTION_ERROR = 1;
656 if (h->prev_ss_state != LWSSSCS_CONNECTED) {
657 r = lws_ss_event_helper(h, LWSSSCS_CONNECTED);
658 if (r != LWSSSSRET_OK)
659 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
660 }
661 }
662
663 /*
664 * Since it's an http transaction we initiated... this is
665 * proof of connection validity
666 */
667 lws_validity_confirmed(wsi);
668
669 #if defined(LWS_WITH_SS_RIDESHARE)
670
671 /*
672 * There are two ways we might want to deal with multipart,
673 * one is pass it through raw (although the user code needs
674 * a helping hand for learning the boundary), and the other
675 * is to deframe it and provide basically submessages in the
676 * different parts.
677 */
678
679 if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf),
680 WSI_TOKEN_HTTP_CONTENT_TYPE) > 0 &&
681 /* multipart/form-data;
682 * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
683
684 (!strncmp((char *)buf, "multipart/form-data", 19) ||
685 !strncmp((char *)buf, "multipart/related", 17))) {
686 struct lws_tokenize ts;
687 lws_tokenize_elem e;
688
689 // puts((const char *)buf);
690
691 memset(&ts, 0, sizeof(ts));
692 ts.start = (char *)buf;
693 ts.len = strlen(ts.start);
694 ts.flags = LWS_TOKENIZE_F_RFC7230_DELIMS |
695 LWS_TOKENIZE_F_SLASH_NONTERM |
696 LWS_TOKENIZE_F_MINUS_NONTERM;
697
698 h->u.http.boundary[0] = '\0';
699 do {
700 e = lws_tokenize(&ts);
701 if (e == LWS_TOKZE_TOKEN_NAME_EQUALS &&
702 !strncmp(ts.token, "boundary", 8) &&
703 ts.token_len == 8) {
704 e = lws_tokenize(&ts);
705 if (e != LWS_TOKZE_TOKEN)
706 goto malformed;
707 h->u.http.boundary[0] = '\x0d';
708 h->u.http.boundary[1] = '\x0a';
709 h->u.http.boundary[2] = '-';
710 h->u.http.boundary[3] = '-';
711 lws_strnncpy(h->u.http.boundary + 4,
712 ts.token, ts.token_len,
713 sizeof(h->u.http.boundary) - 4);
714 h->u.http.boundary_len =
715 (uint8_t)(ts.token_len + 4);
716 h->u.http.boundary_seq = 2;
717 h->u.http.boundary_dashes = 0;
718 }
719 } while (e > 0);
720 lwsl_info("%s: multipart boundary '%s' len %d\n", __func__,
721 h->u.http.boundary, h->u.http.boundary_len);
722
723 /* inform the ss that a related message group begins */
724
725 if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) &&
726 h->u.http.boundary[0])
727 h->info.rx(ss_to_userobj(h), NULL, 0,
728 LWSSS_FLAG_RELATED_START);
729
730 // lws_header_table_detach(wsi, 0);
731 }
732 break;
733 malformed:
734 lwsl_notice("%s: malformed multipart header\n", __func__);
735 return -1;
736 #else
737 break;
738 #endif
739
740 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
741 if (!h)
742 return -1;
743 if (h->writeable_len)
744 wsi->http.writeable_len = h->writeable_len;
745
746 {
747 uint8_t **p = (uint8_t **)in, *end = (*p) + len,
748 *oin = *(uint8_t **)in;
749
750 /*
751 * blob-based headers
752 */
753
754 for (m = 0; m < _LWSSS_HBI_COUNT; m++) {
755 lws_system_blob_t *ab;
756 int o = 0, n;
757
758 if (!h->policy->u.http.blob_header[m])
759 continue;
760
761 /*
762 * To be backward compatible, default is system-wide LWA auth,
763 * and "http_auth_header" is for default LWA auth, current users do not
764 * need any change in their policy.
765 * If user wants different auth/token, need to specify the "use_auth"
766 * and will be handled after metadata headers are applied.
767 */
768
769 if (m == LWSSS_HBI_AUTH &&
770 h->policy->u.http.auth_preamble)
771 o = lws_snprintf((char *)buf, sizeof(buf), "%s",
772 h->policy->u.http.auth_preamble);
773
774 if (o > (int)sizeof(buf) - 2)
775 return -1;
776
777 ab = lws_system_get_blob(wsi->a.context, blob_idx[m], 0);
778 if (!ab)
779 return -1;
780
781 buflen = sizeof(buf) - (unsigned int)o - 2u;
782 n = lws_system_blob_get(ab, buf + o, &buflen, 0);
783 if (n < 0)
784 return -1;
785
786 buf[(unsigned int)o + buflen] = '\0';
787 lwsl_debug("%s: adding blob %d: %s\n", __func__, m, buf);
788
789 if (lws_add_http_header_by_name(wsi,
790 (uint8_t *)h->policy->u.http.blob_header[m],
791 buf, (int)((int)buflen + o), p, end))
792 return -1;
793 }
794
795 /*
796 * metadata-based headers
797 */
798
799 if (lws_apply_metadata(h, wsi, buf, p, end))
800 return -1;
801
802 #if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
803 if (h->policy->flags & LWSSSPOLF_DIRECT_PROTO_STR) {
804 if (lws_apply_instant_metadata(h, wsi, buf, p, end))
805 return -1;
806 }
807 #endif
808
809 #if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
810 if (h->policy->auth && h->policy->auth->type &&
811 !strcmp(h->policy->auth->type, "sigv4")) {
812
813 if (lws_ss_apply_sigv4(wsi, h, p, end))
814 return -1;
815 }
816 #endif
817
818
819 (void)oin;
820 //if (*p != oin)
821 // lwsl_hexdump_notice(oin, lws_ptr_diff_size_t(*p, oin));
822
823 }
824
825 /*
826 * So when proxied, for POST we have to synthesize a CONNECTED
827 * state, so it can request a writeable and deliver the POST
828 * body
829 */
830 if ((h->policy->protocol == LWSSSP_H1 ||
831 h->policy->protocol == LWSSSP_H2) &&
832 h->being_serialized && (
833 !strcmp(h->policy->u.http.method, "PUT") ||
834 !strcmp(h->policy->u.http.method, "PATCH") ||
835 !strcmp(h->policy->u.http.method, "POST"))) {
836
837 wsi->client_suppress_CONNECTION_ERROR = 1;
838 if (h->prev_ss_state != LWSSSCS_CONNECTED) {
839 r = lws_ss_event_helper(h, LWSSSCS_CONNECTED);
840 if (r)
841 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
842 }
843 }
844
845 break;
846
847 /* chunks of chunked content, with header removed */
848 case LWS_CALLBACK_HTTP_BODY:
849 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
850 lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ: read %d\n",
851 __func__, (int)len);
852 if (!h || !h->info.rx)
853 return 0;
854
855 #if defined(LWS_WITH_SS_RIDESHARE)
856 if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) &&
857 h->u.http.boundary[0])
858 return ss_http_multipart_parser(h, in, len);
859 #endif
860
861 if (!h->subseq) {
862 f |= LWSSS_FLAG_SOM;
863 h->hanging_som = 1;
864 h->subseq = 1;
865 }
866
867 // lwsl_notice("%s: HTTP_READ: client side sent len %d fl 0x%x\n",
868 // __func__, (int)len, (int)f);
869
870 h->wsi = wsi; /* since we accept the wsi is bound to the SS,
871 * ensure the SS feels the same way about the wsi */
872 r = h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f);
873 if (r != LWSSSSRET_OK)
874 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
875
876 return 0; /* don't passthru */
877
878 /* uninterpreted http content */
879 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
880 {
881 char *px = (char *)buf + LWS_PRE; /* guarantees LWS_PRE */
882 int lenx = sizeof(buf) - LWS_PRE;
883
884 m = lws_http_client_read(wsi, &px, &lenx);
885 if (m < 0)
886 return m;
887 }
888 lws_set_timeout(wsi, 99, 30);
889
890 return 0; /* don't passthru */
891
892 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
893 // lwsl_debug("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n", __func__);
894
895 if (!h)
896 return -1;
897
898 if (h->hanging_som) {
899 h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM);
900 h->hanging_som = 0;
901 h->subseq = 0;
902 }
903
904 wsi->http.writeable_len = h->writeable_len = 0;
905 lws_sul_cancel(&h->sul_timeout);
906
907 h->txn_ok = 1;
908
909 #if defined(LWS_WITH_SYS_METRICS)
910 lws_metrics_tag_ss_add(h, "result",
911 h->u.http.good_respcode ?
912 "SS_ACK_REMOTE" : "SS_NACK_REMOTE");
913 #endif
914
915 r = lws_ss_event_helper(h, h->u.http.good_respcode ?
916 LWSSSCS_QOS_ACK_REMOTE :
917 LWSSSCS_QOS_NACK_REMOTE);
918 if (r != LWSSSSRET_OK)
919 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
920
921 lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
922 break;
923
924 case LWS_CALLBACK_HTTP_WRITEABLE:
925 case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
926
927 if (!h || !h->info.tx) {
928 lwsl_notice("%s: no handle / tx\n", __func__);
929 return 0;
930 }
931
932 #if defined(LWS_WITH_SERVER)
933 if (h->txn_resp_pending) {
934 /*
935 * If we're going to start sending something, we need to
936 * to take care of the http response header for it first
937 */
938 h->txn_resp_pending = 0;
939
940 if (lws_add_http_common_headers(wsi,
941 (unsigned int)(h->txn_resp_set ?
942 (h->txn_resp ? h->txn_resp : 200) :
943 HTTP_STATUS_NOT_FOUND),
944 NULL, h->wsi->http.writeable_len,
945 &p, end))
946 return 1;
947
948 /*
949 * metadata-based headers
950 */
951
952 if (lws_apply_metadata(h, wsi, buf, &p, end))
953 return -1;
954
955 if (lws_finalize_write_http_header(wsi, start, &p, end))
956 return 1;
957
958 /* write the body separately */
959 lws_callback_on_writable(wsi);
960
961 return 0;
962 }
963 #endif
964
965 if (
966 #if defined(LWS_WITH_SERVER)
967 !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */
968 #endif
969 !h->rideshare)
970
971 h->rideshare = h->policy;
972
973 #if defined(LWS_WITH_SS_RIDESHARE)
974 if (
975 #if defined(LWS_WITH_SERVER)
976 !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */
977 #endif
978 !h->inside_msg && h->rideshare->u.http.multipart_name)
979 lws_client_http_multipart(wsi,
980 h->rideshare->u.http.multipart_name,
981 h->rideshare->u.http.multipart_filename,
982 h->rideshare->u.http.multipart_content_type,
983 (char **)&p, (char *)end);
984
985 buflen = lws_ptr_diff_size_t(end, p);
986 if (h->policy->u.http.multipart_name)
987 buflen -= 24; /* allow space for end of multipart */
988 #else
989 buflen = lws_ptr_diff_size_t(end, p);
990 #endif
991 r = h->info.tx(ss_to_userobj(h), h->txord++, p, &buflen, &f);
992 if (r == LWSSSSRET_TX_DONT_SEND)
993 return 0;
994 if (r < 0)
995 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
996
997 // lwsl_notice("%s: WRITEABLE: user tx says len %d fl 0x%x\n",
998 // __func__, (int)buflen, (int)f);
999
1000 p += buflen;
1001
1002 if (f & LWSSS_FLAG_EOM) {
1003 #if defined(LWS_WITH_SERVER)
1004 if (!(h->info.flags & LWSSSINFLAGS_ACCEPTED)) {
1005 #endif
1006 conceal_eom = 1;
1007 /* end of rideshares */
1008 if (!h->rideshare->rideshare_streamtype) {
1009 lws_client_http_body_pending(wsi, 0);
1010 #if defined(LWS_WITH_SS_RIDESHARE)
1011 if (h->rideshare->u.http.multipart_name)
1012 lws_client_http_multipart(wsi, NULL, NULL, NULL,
1013 (char **)&p, (char *)end);
1014 conceal_eom = 0;
1015 #endif
1016 } else {
1017 h->rideshare = lws_ss_policy_lookup(wsi->a.context,
1018 h->rideshare->rideshare_streamtype);
1019 lws_callback_on_writable(wsi);
1020 }
1021 #if defined(LWS_WITH_SERVER)
1022 }
1023 #endif
1024
1025 h->inside_msg = 0;
1026 } else {
1027 /* otherwise we can spin with zero length writes */
1028 if (!f && !lws_ptr_diff(p, buf + LWS_PRE))
1029 break;
1030 h->inside_msg = 1;
1031 lws_callback_on_writable(wsi);
1032 }
1033
1034 lwsl_info("%s: lws_write %d %d\n", __func__,
1035 lws_ptr_diff(p, buf + LWS_PRE), f);
1036
1037 if (lws_write(wsi, buf + LWS_PRE, lws_ptr_diff_size_t(p, buf + LWS_PRE),
1038 (!conceal_eom && (f & LWSSS_FLAG_EOM)) ?
1039 LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP) !=
1040 (int)lws_ptr_diff(p, buf + LWS_PRE)) {
1041 lwsl_err("%s: write failed\n", __func__);
1042 return -1;
1043 }
1044
1045 #if defined(LWS_WITH_SERVER)
1046 if ((h->info.flags & LWSSSINFLAGS_ACCEPTED) /* server */ &&
1047 (f & LWSSS_FLAG_EOM) &&
1048 lws_http_transaction_completed(wsi))
1049 return -1;
1050 #else
1051 lws_set_timeout(wsi, 0, 0);
1052 #endif
1053 break;
1054
1055 #if defined(LWS_WITH_SERVER)
1056 case LWS_CALLBACK_HTTP:
1057
1058 if (!h)
1059 return -1;
1060
1061 lwsl_info("%s: LWS_CALLBACK_HTTP\n", __func__);
1062 {
1063
1064 h->txn_resp_set = 0;
1065 h->txn_resp_pending = 1;
1066 h->writeable_len = 0;
1067
1068 #if defined(LWS_ROLE_H2)
1069 m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
1070 if (m) {
1071 if (lws_ss_alloc_set_metadata(h, "method",
1072 lws_hdr_simple_ptr(wsi,
1073 WSI_TOKEN_HTTP_COLON_METHOD), (unsigned int)m))
1074 return -1;
1075 m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH);
1076 if (m && lws_ss_alloc_set_metadata(h, "path",
1077 lws_hdr_simple_ptr(wsi,
1078 WSI_TOKEN_HTTP_COLON_PATH), (unsigned int)m))
1079 return -1;
1080 } else
1081 #endif
1082 {
1083 m = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
1084 if (m) {
1085 if (lws_ss_alloc_set_metadata(h, "path",
1086 lws_hdr_simple_ptr(wsi,
1087 WSI_TOKEN_GET_URI), (unsigned int)m))
1088 return -1;
1089 if (lws_ss_alloc_set_metadata(h, "method", "GET", 3))
1090 return -1;
1091 } else {
1092 m = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
1093 if (m) {
1094 if (lws_ss_alloc_set_metadata(h, "path",
1095 lws_hdr_simple_ptr(wsi,
1096 WSI_TOKEN_POST_URI), (unsigned int)m))
1097 return -1;
1098 if (lws_ss_alloc_set_metadata(h, "method", "POST", 4))
1099 return -1;
1100 } else {
1101 m = lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI);
1102 if (m) {
1103 if (lws_ss_alloc_set_metadata(h, "path",
1104 lws_hdr_simple_ptr(wsi,
1105 WSI_TOKEN_PATCH_URI), (unsigned int)m))
1106 return -1;
1107 if (lws_ss_alloc_set_metadata(h, "method", "PATCH", 5))
1108 return -1;
1109 }
1110 }
1111 }
1112 }
1113 }
1114
1115 if (!h->ss_dangling_connected) {
1116 #if defined(LWS_WITH_SYS_METRICS)
1117 /*
1118 * If any hanging caliper measurement, dump it, and free any tags
1119 */
1120 lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL);
1121 #endif
1122 wsi->client_suppress_CONNECTION_ERROR = 1;
1123 if (h->prev_ss_state != LWSSSCS_CONNECTED) {
1124 r = lws_ss_event_helper(h, LWSSSCS_CONNECTED);
1125 if (r)
1126 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
1127 }
1128 }
1129
1130 r = lws_ss_event_helper(h, LWSSSCS_SERVER_TXN);
1131 if (r)
1132 return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r,
1133 wsi, &h);
1134
1135 return 0;
1136 #endif
1137
1138 default:
1139 break;
1140 }
1141
1142 return lws_callback_http_dummy(wsi, reason, user, in, len);
1143 }
1144
1145 const struct lws_protocols protocol_secstream_h1 = {
1146 "lws-secstream-h1",
1147 secstream_h1,
1148 0, 0, 0, NULL, 0
1149 };
1150
1151 /*
1152 * Munge connect info according to protocol-specific considerations... this
1153 * usually means interpreting aux in a protocol-specific way and using the
1154 * pieces at connection setup time, eg, http url pieces.
1155 *
1156 * len bytes of buf can be used for things with scope until after the actual
1157 * connect.
1158 */
1159
1160 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)1161 secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len,
1162 struct lws_client_connect_info *i,
1163 union lws_ss_contemp *ct)
1164 {
1165 const char *pbasis = h->policy->u.http.url;
1166 size_t used_in, used_out;
1167 lws_strexp_t exp;
1168
1169 /* i.path on entry is used to override the policy urlpath if not "" */
1170
1171 if (i->path[0])
1172 pbasis = i->path;
1173
1174 if (!pbasis)
1175 return 0;
1176
1177 /* uncomment to force h1 */
1178 // i->alpn = "http/1.1";
1179
1180 #if defined(LWS_WITH_SS_RIDESHARE)
1181 if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART)
1182 i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME;
1183
1184 if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED)
1185 i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED;
1186 #endif
1187
1188 if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES)
1189 i->ssl_connection |= LCCSCF_CACHE_COOKIES;
1190
1191 /* protocol aux is the path part */
1192
1193 i->path = buf;
1194
1195 /* skip the unnessary '/' */
1196 if (*pbasis == '/')
1197 pbasis = pbasis + 1;
1198
1199 buf[0] = '/';
1200
1201 lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1);
1202
1203 if (lws_strexp_expand(&exp, pbasis, strlen(pbasis),
1204 &used_in, &used_out) != LSTRX_DONE)
1205 return 1;
1206
1207 return 0;
1208 }
1209
1210
1211 const struct ss_pcols ss_pcol_h1 = {
1212 "h1",
1213 "http/1.1",
1214 &protocol_secstream_h1,
1215 secstream_connect_munge_h1,
1216 NULL, NULL
1217 };
1218