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) 34static int 35ss_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 149around: 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 194static int 195lws_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 213static int 214lws_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) 276static int 277lws_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 313static int 314lws_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 420static 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 427int 428secstream_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; 733malformed: 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 1145const 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 1160static int 1161secstream_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 1211const 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