ws.c (52075B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 #include "curl_setup.h" 25 #include <curl/curl.h> 26 27 #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) 28 29 #include "urldata.h" 30 #include "url.h" 31 #include "bufq.h" 32 #include "curlx/dynbuf.h" 33 #include "rand.h" 34 #include "curlx/base64.h" 35 #include "connect.h" 36 #include "sendf.h" 37 #include "multiif.h" 38 #include "ws.h" 39 #include "easyif.h" 40 #include "transfer.h" 41 #include "select.h" 42 #include "curlx/nonblock.h" 43 #include "curlx/strparse.h" 44 45 /* The last 3 #include files should be in this order */ 46 #include "curl_printf.h" 47 #include "curl_memory.h" 48 #include "memdebug.h" 49 50 51 /*** 52 RFC 6455 Section 5.2 53 54 0 1 2 3 4 5 6 7 55 +-+-+-+-+-------+ 56 |F|R|R|R| opcode| 57 |I|S|S|S| (4) | 58 |N|V|V|V| | 59 | |1|2|3| | 60 */ 61 #define WSBIT_FIN (0x80) 62 #define WSBIT_RSV1 (0x40) 63 #define WSBIT_RSV2 (0x20) 64 #define WSBIT_RSV3 (0x10) 65 #define WSBIT_RSV_MASK (WSBIT_RSV1 | WSBIT_RSV2 | WSBIT_RSV3) 66 #define WSBIT_OPCODE_CONT (0x0) 67 #define WSBIT_OPCODE_TEXT (0x1) 68 #define WSBIT_OPCODE_BIN (0x2) 69 #define WSBIT_OPCODE_CLOSE (0x8) 70 #define WSBIT_OPCODE_PING (0x9) 71 #define WSBIT_OPCODE_PONG (0xa) 72 #define WSBIT_OPCODE_MASK (0xf) 73 74 #define WSBIT_MASK 0x80 75 76 /* buffer dimensioning */ 77 #define WS_CHUNK_SIZE 65535 78 #define WS_CHUNK_COUNT 2 79 80 81 /* a client-side WS frame decoder, parsing frame headers and 82 * payload, keeping track of current position and stats */ 83 enum ws_dec_state { 84 WS_DEC_INIT, 85 WS_DEC_HEAD, 86 WS_DEC_PAYLOAD 87 }; 88 89 struct ws_decoder { 90 int frame_age; /* zero */ 91 int frame_flags; /* See the CURLWS_* defines */ 92 curl_off_t payload_offset; /* the offset parsing is at */ 93 curl_off_t payload_len; 94 unsigned char head[10]; 95 int head_len, head_total; 96 enum ws_dec_state state; 97 int cont_flags; 98 }; 99 100 /* a client-side WS frame encoder, generating frame headers and 101 * converting payloads, tracking remaining data in current frame */ 102 struct ws_encoder { 103 curl_off_t payload_len; /* payload length of current frame */ 104 curl_off_t payload_remain; /* remaining payload of current */ 105 unsigned int xori; /* xor index */ 106 unsigned char mask[4]; /* 32-bit mask for this connection */ 107 unsigned char firstbyte; /* first byte of frame we encode */ 108 BIT(contfragment); /* set TRUE if the previous fragment sent was not final */ 109 }; 110 111 /* A websocket connection with en- and decoder that treat frames 112 * and keep track of boundaries. */ 113 struct websocket { 114 struct Curl_easy *data; /* used for write callback handling */ 115 struct ws_decoder dec; /* decode of we frames */ 116 struct ws_encoder enc; /* decode of we frames */ 117 struct bufq recvbuf; /* raw data from the server */ 118 struct bufq sendbuf; /* raw data to be sent to the server */ 119 struct curl_ws_frame frame; /* the current WS FRAME received */ 120 size_t sendbuf_payload; /* number of payload bytes in sendbuf */ 121 }; 122 123 124 static const char *ws_frame_name_of_op(unsigned char firstbyte) 125 { 126 switch(firstbyte & WSBIT_OPCODE_MASK) { 127 case WSBIT_OPCODE_CONT: 128 return "CONT"; 129 case WSBIT_OPCODE_TEXT: 130 return "TEXT"; 131 case WSBIT_OPCODE_BIN: 132 return "BIN"; 133 case WSBIT_OPCODE_CLOSE: 134 return "CLOSE"; 135 case WSBIT_OPCODE_PING: 136 return "PING"; 137 case WSBIT_OPCODE_PONG: 138 return "PONG"; 139 default: 140 return "???"; 141 } 142 } 143 144 static int ws_frame_firstbyte2flags(struct Curl_easy *data, 145 unsigned char firstbyte, int cont_flags) 146 { 147 switch(firstbyte) { 148 /* 0x00 - intermediate TEXT/BINARY fragment */ 149 case WSBIT_OPCODE_CONT: 150 if(!(cont_flags & CURLWS_CONT)) { 151 failf(data, "[WS] no ongoing fragmented message to resume"); 152 return 0; 153 } 154 return cont_flags | CURLWS_CONT; 155 /* 0x80 - final TEXT/BIN fragment */ 156 case (WSBIT_OPCODE_CONT | WSBIT_FIN): 157 if(!(cont_flags & CURLWS_CONT)) { 158 failf(data, "[WS] no ongoing fragmented message to resume"); 159 return 0; 160 } 161 return cont_flags & ~CURLWS_CONT; 162 /* 0x01 - first TEXT fragment */ 163 case WSBIT_OPCODE_TEXT: 164 if(cont_flags & CURLWS_CONT) { 165 failf(data, "[WS] fragmented message interrupted by new TEXT msg"); 166 return 0; 167 } 168 return CURLWS_TEXT | CURLWS_CONT; 169 /* 0x81 - unfragmented TEXT msg */ 170 case (WSBIT_OPCODE_TEXT | WSBIT_FIN): 171 if(cont_flags & CURLWS_CONT) { 172 failf(data, "[WS] fragmented message interrupted by new TEXT msg"); 173 return 0; 174 } 175 return CURLWS_TEXT; 176 /* 0x02 - first BINARY fragment */ 177 case WSBIT_OPCODE_BIN: 178 if(cont_flags & CURLWS_CONT) { 179 failf(data, "[WS] fragmented message interrupted by new BINARY msg"); 180 return 0; 181 } 182 return CURLWS_BINARY | CURLWS_CONT; 183 /* 0x82 - unfragmented BINARY msg */ 184 case (WSBIT_OPCODE_BIN | WSBIT_FIN): 185 if(cont_flags & CURLWS_CONT) { 186 failf(data, "[WS] fragmented message interrupted by new BINARY msg"); 187 return 0; 188 } 189 return CURLWS_BINARY; 190 /* 0x08 - first CLOSE fragment */ 191 case WSBIT_OPCODE_CLOSE: 192 failf(data, "[WS] invalid fragmented CLOSE frame"); 193 return 0; 194 /* 0x88 - unfragmented CLOSE */ 195 case (WSBIT_OPCODE_CLOSE | WSBIT_FIN): 196 return CURLWS_CLOSE; 197 /* 0x09 - first PING fragment */ 198 case WSBIT_OPCODE_PING: 199 failf(data, "[WS] invalid fragmented PING frame"); 200 return 0; 201 /* 0x89 - unfragmented PING */ 202 case (WSBIT_OPCODE_PING | WSBIT_FIN): 203 return CURLWS_PING; 204 /* 0x0a - first PONG fragment */ 205 case WSBIT_OPCODE_PONG: 206 failf(data, "[WS] invalid fragmented PONG frame"); 207 return 0; 208 /* 0x8a - unfragmented PONG */ 209 case (WSBIT_OPCODE_PONG | WSBIT_FIN): 210 return CURLWS_PONG; 211 /* invalid first byte */ 212 default: 213 if(firstbyte & WSBIT_RSV_MASK) 214 /* any of the reserved bits 0x40/0x20/0x10 are set */ 215 failf(data, "[WS] invalid reserved bits: %02x", firstbyte); 216 else 217 /* any of the reserved opcodes 0x3-0x7 or 0xb-0xf is used */ 218 failf(data, "[WS] invalid opcode: %02x", firstbyte); 219 return 0; 220 } 221 } 222 223 static unsigned char ws_frame_flags2firstbyte(struct Curl_easy *data, 224 unsigned int flags, 225 bool contfragment, 226 CURLcode *err) 227 { 228 switch(flags & ~CURLWS_OFFSET) { 229 case 0: 230 if(contfragment) { 231 infof(data, "[WS] no flags given; interpreting as continuation " 232 "fragment for compatibility"); 233 return (WSBIT_OPCODE_CONT | WSBIT_FIN); 234 } 235 failf(data, "[WS] no flags given"); 236 *err = CURLE_BAD_FUNCTION_ARGUMENT; 237 return 0xff; 238 case CURLWS_CONT: 239 if(contfragment) { 240 infof(data, "[WS] setting CURLWS_CONT flag without message type is " 241 "supported for compatibility but highly discouraged"); 242 return WSBIT_OPCODE_CONT; 243 } 244 failf(data, "[WS] No ongoing fragmented message to continue"); 245 *err = CURLE_BAD_FUNCTION_ARGUMENT; 246 return 0xff; 247 case CURLWS_TEXT: 248 return contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN) 249 : (WSBIT_OPCODE_TEXT | WSBIT_FIN); 250 case (CURLWS_TEXT | CURLWS_CONT): 251 return contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_TEXT; 252 case CURLWS_BINARY: 253 return contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN) 254 : (WSBIT_OPCODE_BIN | WSBIT_FIN); 255 case (CURLWS_BINARY | CURLWS_CONT): 256 return contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_BIN; 257 case CURLWS_CLOSE: 258 return WSBIT_OPCODE_CLOSE | WSBIT_FIN; 259 case (CURLWS_CLOSE | CURLWS_CONT): 260 failf(data, "[WS] CLOSE frame must not be fragmented"); 261 *err = CURLE_BAD_FUNCTION_ARGUMENT; 262 return 0xff; 263 case CURLWS_PING: 264 return WSBIT_OPCODE_PING | WSBIT_FIN; 265 case (CURLWS_PING | CURLWS_CONT): 266 failf(data, "[WS] PING frame must not be fragmented"); 267 *err = CURLE_BAD_FUNCTION_ARGUMENT; 268 return 0xff; 269 case CURLWS_PONG: 270 return WSBIT_OPCODE_PONG | WSBIT_FIN; 271 case (CURLWS_PONG | CURLWS_CONT): 272 failf(data, "[WS] PONG frame must not be fragmented"); 273 *err = CURLE_BAD_FUNCTION_ARGUMENT; 274 return 0xff; 275 default: 276 failf(data, "[WS] unknown flags: %x", flags); 277 *err = CURLE_BAD_FUNCTION_ARGUMENT; 278 return 0xff; 279 } 280 } 281 282 static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, 283 const char *msg) 284 { 285 switch(dec->head_len) { 286 case 0: 287 break; 288 case 1: 289 CURL_TRC_WS(data, "decoded %s [%s%s]", msg, 290 ws_frame_name_of_op(dec->head[0]), 291 (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL"); 292 break; 293 default: 294 if(dec->head_len < dec->head_total) { 295 CURL_TRC_WS(data, "decoded %s [%s%s](%d/%d)", msg, 296 ws_frame_name_of_op(dec->head[0]), 297 (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL", 298 dec->head_len, dec->head_total); 299 } 300 else { 301 CURL_TRC_WS(data, "decoded %s [%s%s payload=%" 302 FMT_OFF_T "/%" FMT_OFF_T "]", 303 msg, ws_frame_name_of_op(dec->head[0]), 304 (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL", 305 dec->payload_offset, dec->payload_len); 306 } 307 break; 308 } 309 } 310 311 static CURLcode ws_send_raw_blocking(CURL *data, struct websocket *ws, 312 const char *buffer, size_t buflen); 313 314 typedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen, 315 int frame_age, int frame_flags, 316 curl_off_t payload_offset, 317 curl_off_t payload_len, 318 void *userp, 319 CURLcode *err); 320 321 static void ws_dec_next_frame(struct ws_decoder *dec) 322 { 323 dec->frame_age = 0; 324 dec->frame_flags = 0; 325 dec->payload_offset = 0; 326 dec->payload_len = 0; 327 dec->head_len = dec->head_total = 0; 328 dec->state = WS_DEC_INIT; 329 /* dec->cont_flags must be carried over to next frame */ 330 } 331 332 static void ws_dec_reset(struct ws_decoder *dec) 333 { 334 dec->frame_age = 0; 335 dec->frame_flags = 0; 336 dec->payload_offset = 0; 337 dec->payload_len = 0; 338 dec->head_len = dec->head_total = 0; 339 dec->state = WS_DEC_INIT; 340 dec->cont_flags = 0; 341 } 342 343 static void ws_dec_init(struct ws_decoder *dec) 344 { 345 ws_dec_reset(dec); 346 } 347 348 static CURLcode ws_dec_read_head(struct ws_decoder *dec, 349 struct Curl_easy *data, 350 struct bufq *inraw) 351 { 352 const unsigned char *inbuf; 353 size_t inlen; 354 355 while(Curl_bufq_peek(inraw, &inbuf, &inlen)) { 356 if(dec->head_len == 0) { 357 dec->head[0] = *inbuf; 358 Curl_bufq_skip(inraw, 1); 359 360 dec->frame_flags = ws_frame_firstbyte2flags(data, dec->head[0], 361 dec->cont_flags); 362 if(!dec->frame_flags) { 363 ws_dec_reset(dec); 364 return CURLE_RECV_ERROR; 365 } 366 367 /* fragmentation only applies to data frames (text/binary); 368 * control frames (close/ping/pong) do not affect the CONT status */ 369 if(dec->frame_flags & (CURLWS_TEXT | CURLWS_BINARY)) { 370 dec->cont_flags = dec->frame_flags; 371 } 372 373 dec->head_len = 1; 374 /* ws_dec_info(dec, data, "seeing opcode"); */ 375 continue; 376 } 377 else if(dec->head_len == 1) { 378 dec->head[1] = *inbuf; 379 Curl_bufq_skip(inraw, 1); 380 dec->head_len = 2; 381 382 if(dec->head[1] & WSBIT_MASK) { 383 /* A client MUST close a connection if it detects a masked frame. */ 384 failf(data, "[WS] masked input frame"); 385 ws_dec_reset(dec); 386 return CURLE_RECV_ERROR; 387 } 388 if(dec->frame_flags & CURLWS_PING && dec->head[1] > 125) { 389 /* The maximum valid size of PING frames is 125 bytes. 390 Accepting overlong pings would mean sending equivalent pongs! */ 391 failf(data, "[WS] received PING frame is too big"); 392 ws_dec_reset(dec); 393 return CURLE_RECV_ERROR; 394 } 395 if(dec->frame_flags & CURLWS_PONG && dec->head[1] > 125) { 396 /* The maximum valid size of PONG frames is 125 bytes. */ 397 failf(data, "[WS] received PONG frame is too big"); 398 ws_dec_reset(dec); 399 return CURLE_RECV_ERROR; 400 } 401 if(dec->frame_flags & CURLWS_CLOSE && dec->head[1] > 125) { 402 /* The maximum valid size of CLOSE frames is 125 bytes. */ 403 failf(data, "[WS] received CLOSE frame is too big"); 404 ws_dec_reset(dec); 405 return CURLE_RECV_ERROR; 406 } 407 408 /* How long is the frame head? */ 409 if(dec->head[1] == 126) { 410 dec->head_total = 4; 411 continue; 412 } 413 else if(dec->head[1] == 127) { 414 dec->head_total = 10; 415 continue; 416 } 417 else { 418 dec->head_total = 2; 419 } 420 } 421 422 if(dec->head_len < dec->head_total) { 423 dec->head[dec->head_len] = *inbuf; 424 Curl_bufq_skip(inraw, 1); 425 ++dec->head_len; 426 if(dec->head_len < dec->head_total) { 427 /* ws_dec_info(dec, data, "decoding head"); */ 428 continue; 429 } 430 } 431 /* got the complete frame head */ 432 DEBUGASSERT(dec->head_len == dec->head_total); 433 switch(dec->head_total) { 434 case 2: 435 dec->payload_len = dec->head[1]; 436 break; 437 case 4: 438 dec->payload_len = (dec->head[2] << 8) | dec->head[3]; 439 break; 440 case 10: 441 if(dec->head[2] > 127) { 442 failf(data, "[WS] frame length longer than 64 signed not supported"); 443 return CURLE_RECV_ERROR; 444 } 445 dec->payload_len = ((curl_off_t)dec->head[2] << 56) | 446 (curl_off_t)dec->head[3] << 48 | 447 (curl_off_t)dec->head[4] << 40 | 448 (curl_off_t)dec->head[5] << 32 | 449 (curl_off_t)dec->head[6] << 24 | 450 (curl_off_t)dec->head[7] << 16 | 451 (curl_off_t)dec->head[8] << 8 | 452 dec->head[9]; 453 break; 454 default: 455 /* this should never happen */ 456 DEBUGASSERT(0); 457 failf(data, "[WS] unexpected frame header length"); 458 return CURLE_RECV_ERROR; 459 } 460 461 dec->frame_age = 0; 462 dec->payload_offset = 0; 463 ws_dec_info(dec, data, "decoded"); 464 return CURLE_OK; 465 } 466 return CURLE_AGAIN; 467 } 468 469 static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, 470 struct Curl_easy *data, 471 struct bufq *inraw, 472 ws_write_payload *write_payload, 473 void *write_ctx) 474 { 475 const unsigned char *inbuf; 476 size_t inlen; 477 ssize_t nwritten; 478 CURLcode result; 479 curl_off_t remain = dec->payload_len - dec->payload_offset; 480 481 (void)data; 482 while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) { 483 if((curl_off_t)inlen > remain) 484 inlen = (size_t)remain; 485 nwritten = write_payload(inbuf, inlen, dec->frame_age, dec->frame_flags, 486 dec->payload_offset, dec->payload_len, 487 write_ctx, &result); 488 if(nwritten < 0) 489 return result; 490 Curl_bufq_skip(inraw, (size_t)nwritten); 491 dec->payload_offset += (curl_off_t)nwritten; 492 remain = dec->payload_len - dec->payload_offset; 493 CURL_TRC_WS(data, "passed %zd bytes payload, %" 494 FMT_OFF_T " remain", nwritten, remain); 495 } 496 497 return remain ? CURLE_AGAIN : CURLE_OK; 498 } 499 500 static CURLcode ws_dec_pass(struct ws_decoder *dec, 501 struct Curl_easy *data, 502 struct bufq *inraw, 503 ws_write_payload *write_payload, 504 void *write_ctx) 505 { 506 CURLcode result; 507 508 if(Curl_bufq_is_empty(inraw)) 509 return CURLE_AGAIN; 510 511 switch(dec->state) { 512 case WS_DEC_INIT: 513 ws_dec_next_frame(dec); 514 dec->state = WS_DEC_HEAD; 515 FALLTHROUGH(); 516 case WS_DEC_HEAD: 517 result = ws_dec_read_head(dec, data, inraw); 518 if(result) { 519 if(result != CURLE_AGAIN) { 520 infof(data, "[WS] decode error %d", (int)result); 521 break; /* real error */ 522 } 523 /* incomplete ws frame head */ 524 DEBUGASSERT(Curl_bufq_is_empty(inraw)); 525 break; 526 } 527 /* head parsing done */ 528 dec->state = WS_DEC_PAYLOAD; 529 if(dec->payload_len == 0) { 530 ssize_t nwritten; 531 const unsigned char tmp = '\0'; 532 /* special case of a 0 length frame, need to write once */ 533 nwritten = write_payload(&tmp, 0, dec->frame_age, dec->frame_flags, 534 0, 0, write_ctx, &result); 535 if(nwritten < 0) 536 return result; 537 dec->state = WS_DEC_INIT; 538 break; 539 } 540 FALLTHROUGH(); 541 case WS_DEC_PAYLOAD: 542 result = ws_dec_pass_payload(dec, data, inraw, write_payload, write_ctx); 543 ws_dec_info(dec, data, "passing"); 544 if(result) 545 return result; 546 /* payload parsing done */ 547 dec->state = WS_DEC_INIT; 548 break; 549 default: 550 /* we covered all enums above, but some code analyzers are whimps */ 551 result = CURLE_FAILED_INIT; 552 } 553 return result; 554 } 555 556 static void update_meta(struct websocket *ws, 557 int frame_age, int frame_flags, 558 curl_off_t payload_offset, 559 curl_off_t payload_len, 560 size_t cur_len) 561 { 562 curl_off_t bytesleft = (payload_len - payload_offset - cur_len); 563 564 ws->frame.age = frame_age; 565 ws->frame.flags = frame_flags; 566 ws->frame.offset = payload_offset; 567 ws->frame.len = cur_len; 568 ws->frame.bytesleft = bytesleft; 569 } 570 571 /* WebSockets decoding client writer */ 572 struct ws_cw_ctx { 573 struct Curl_cwriter super; 574 struct bufq buf; 575 }; 576 577 static CURLcode ws_cw_init(struct Curl_easy *data, 578 struct Curl_cwriter *writer) 579 { 580 struct ws_cw_ctx *ctx = writer->ctx; 581 (void)data; 582 Curl_bufq_init2(&ctx->buf, WS_CHUNK_SIZE, 1, BUFQ_OPT_SOFT_LIMIT); 583 return CURLE_OK; 584 } 585 586 static void ws_cw_close(struct Curl_easy *data, struct Curl_cwriter *writer) 587 { 588 struct ws_cw_ctx *ctx = writer->ctx; 589 (void) data; 590 Curl_bufq_free(&ctx->buf); 591 } 592 593 struct ws_cw_dec_ctx { 594 struct Curl_easy *data; 595 struct websocket *ws; 596 struct Curl_cwriter *next_writer; 597 int cw_type; 598 }; 599 600 static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen, 601 int frame_age, int frame_flags, 602 curl_off_t payload_offset, 603 curl_off_t payload_len, 604 void *user_data, 605 CURLcode *err) 606 { 607 struct ws_cw_dec_ctx *ctx = user_data; 608 struct Curl_easy *data = ctx->data; 609 struct websocket *ws = ctx->ws; 610 bool auto_pong = !data->set.ws_no_auto_pong; 611 curl_off_t remain = (payload_len - (payload_offset + buflen)); 612 613 (void)frame_age; 614 615 if(auto_pong && (frame_flags & CURLWS_PING) && !remain) { 616 /* auto-respond to PINGs, only works for single-frame payloads atm */ 617 size_t bytes; 618 infof(data, "[WS] auto-respond to PING with a PONG"); 619 /* send back the exact same content as a PONG */ 620 *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG); 621 if(*err) 622 return -1; 623 } 624 else if(buflen || !remain) { 625 /* forward the decoded frame to the next client writer. */ 626 update_meta(ws, frame_age, frame_flags, payload_offset, 627 payload_len, buflen); 628 629 *err = Curl_cwriter_write(data, ctx->next_writer, ctx->cw_type, 630 (const char *)buf, buflen); 631 if(*err) 632 return -1; 633 } 634 *err = CURLE_OK; 635 return (ssize_t)buflen; 636 } 637 638 static CURLcode ws_cw_write(struct Curl_easy *data, 639 struct Curl_cwriter *writer, int type, 640 const char *buf, size_t nbytes) 641 { 642 struct ws_cw_ctx *ctx = writer->ctx; 643 struct websocket *ws; 644 CURLcode result; 645 646 if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode) 647 return Curl_cwriter_write(data, writer->next, type, buf, nbytes); 648 649 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); 650 if(!ws) { 651 failf(data, "[WS] not a websocket transfer"); 652 return CURLE_FAILED_INIT; 653 } 654 655 if(nbytes) { 656 size_t nwritten; 657 result = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf, 658 nbytes, &nwritten); 659 if(result) { 660 infof(data, "WS: error adding data to buffer %d", result); 661 return result; 662 } 663 } 664 665 while(!Curl_bufq_is_empty(&ctx->buf)) { 666 struct ws_cw_dec_ctx pass_ctx; 667 pass_ctx.data = data; 668 pass_ctx.ws = ws; 669 pass_ctx.next_writer = writer->next; 670 pass_ctx.cw_type = type; 671 result = ws_dec_pass(&ws->dec, data, &ctx->buf, 672 ws_cw_dec_next, &pass_ctx); 673 if(result == CURLE_AGAIN) { 674 /* insufficient amount of data, keep it for later. 675 * we pretend to have written all since we have a copy */ 676 CURL_TRC_WS(data, "buffered incomplete frame head"); 677 return CURLE_OK; 678 } 679 else if(result) { 680 infof(data, "[WS] decode error %d", (int)result); 681 return result; 682 } 683 } 684 685 if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { 686 failf(data, "[WS] decode ending with %zd frame bytes remaining", 687 Curl_bufq_len(&ctx->buf)); 688 return CURLE_RECV_ERROR; 689 } 690 691 return CURLE_OK; 692 } 693 694 /* WebSocket payload decoding client writer. */ 695 static const struct Curl_cwtype ws_cw_decode = { 696 "ws-decode", 697 NULL, 698 ws_cw_init, 699 ws_cw_write, 700 ws_cw_close, 701 sizeof(struct ws_cw_ctx) 702 }; 703 704 705 static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, 706 const char *msg) 707 { 708 CURL_TRC_WS(data, "WS-ENC: %s [%s%s payload=%" 709 FMT_OFF_T "/%" FMT_OFF_T "]", 710 msg, ws_frame_name_of_op(enc->firstbyte), 711 (enc->firstbyte & WSBIT_FIN) ? "" : " NON-FIN", 712 enc->payload_len - enc->payload_remain, enc->payload_len); 713 } 714 715 static void ws_enc_reset(struct ws_encoder *enc) 716 { 717 enc->payload_remain = 0; 718 enc->xori = 0; 719 enc->contfragment = FALSE; 720 } 721 722 static void ws_enc_init(struct ws_encoder *enc) 723 { 724 ws_enc_reset(enc); 725 } 726 727 /*** 728 RFC 6455 Section 5.2 729 730 0 1 2 3 731 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 732 +-+-+-+-+-------+-+-------------+-------------------------------+ 733 |F|R|R|R| opcode|M| Payload len | Extended payload length | 734 |I|S|S|S| (4) |A| (7) | (16/64) | 735 |N|V|V|V| |S| | (if payload len==126/127) | 736 | |1|2|3| |K| | | 737 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 738 | Extended payload length continued, if payload len == 127 | 739 + - - - - - - - - - - - - - - - +-------------------------------+ 740 | |Masking-key, if MASK set to 1 | 741 +-------------------------------+-------------------------------+ 742 | Masking-key (continued) | Payload Data | 743 +-------------------------------- - - - - - - - - - - - - - - - + 744 : Payload Data continued ... : 745 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 746 | Payload Data continued ... | 747 +---------------------------------------------------------------+ 748 */ 749 750 static ssize_t ws_enc_write_head(struct Curl_easy *data, 751 struct ws_encoder *enc, 752 unsigned int flags, 753 curl_off_t payload_len, 754 struct bufq *out, 755 CURLcode *err) 756 { 757 unsigned char firstbyte = 0; 758 unsigned char head[14]; 759 size_t hlen, n; 760 761 if(payload_len < 0) { 762 failf(data, "[WS] starting new frame with negative payload length %" 763 FMT_OFF_T, payload_len); 764 *err = CURLE_SEND_ERROR; 765 return -1; 766 } 767 768 if(enc->payload_remain > 0) { 769 /* trying to write a new frame before the previous one is finished */ 770 failf(data, "[WS] starting new frame with %zd bytes from last one " 771 "remaining to be sent", (ssize_t)enc->payload_remain); 772 *err = CURLE_SEND_ERROR; 773 return -1; 774 } 775 776 firstbyte = ws_frame_flags2firstbyte(data, flags, enc->contfragment, err); 777 if(*err) { 778 return -1; 779 } 780 781 /* fragmentation only applies to data frames (text/binary); 782 * control frames (close/ping/pong) do not affect the CONT status */ 783 if(flags & (CURLWS_TEXT | CURLWS_BINARY)) { 784 enc->contfragment = (flags & CURLWS_CONT) ? (bit)TRUE : (bit)FALSE; 785 } 786 787 if(flags & CURLWS_PING && payload_len > 125) { 788 /* The maximum valid size of PING frames is 125 bytes. */ 789 failf(data, "[WS] given PING frame is too big"); 790 *err = CURLE_TOO_LARGE; 791 return -1; 792 } 793 if(flags & CURLWS_PONG && payload_len > 125) { 794 /* The maximum valid size of PONG frames is 125 bytes. */ 795 failf(data, "[WS] given PONG frame is too big"); 796 *err = CURLE_TOO_LARGE; 797 return -1; 798 } 799 if(flags & CURLWS_CLOSE && payload_len > 125) { 800 /* The maximum valid size of CLOSE frames is 125 bytes. */ 801 failf(data, "[WS] given CLOSE frame is too big"); 802 *err = CURLE_TOO_LARGE; 803 return -1; 804 } 805 806 head[0] = enc->firstbyte = firstbyte; 807 if(payload_len > 65535) { 808 head[1] = 127 | WSBIT_MASK; 809 head[2] = (unsigned char)((payload_len >> 56) & 0xff); 810 head[3] = (unsigned char)((payload_len >> 48) & 0xff); 811 head[4] = (unsigned char)((payload_len >> 40) & 0xff); 812 head[5] = (unsigned char)((payload_len >> 32) & 0xff); 813 head[6] = (unsigned char)((payload_len >> 24) & 0xff); 814 head[7] = (unsigned char)((payload_len >> 16) & 0xff); 815 head[8] = (unsigned char)((payload_len >> 8) & 0xff); 816 head[9] = (unsigned char)(payload_len & 0xff); 817 hlen = 10; 818 } 819 else if(payload_len >= 126) { 820 head[1] = 126 | WSBIT_MASK; 821 head[2] = (unsigned char)((payload_len >> 8) & 0xff); 822 head[3] = (unsigned char)(payload_len & 0xff); 823 hlen = 4; 824 } 825 else { 826 head[1] = (unsigned char)payload_len | WSBIT_MASK; 827 hlen = 2; 828 } 829 830 enc->payload_remain = enc->payload_len = payload_len; 831 ws_enc_info(enc, data, "sending"); 832 833 /* add 4 bytes mask */ 834 memcpy(&head[hlen], &enc->mask, 4); 835 hlen += 4; 836 /* reset for payload to come */ 837 enc->xori = 0; 838 839 *err = Curl_bufq_write(out, head, hlen, &n); 840 if(*err) 841 return -1; 842 if(n != hlen) { 843 /* We use a bufq with SOFT_LIMIT, writing should always succeed */ 844 DEBUGASSERT(0); 845 *err = CURLE_SEND_ERROR; 846 return -1; 847 } 848 return (ssize_t)n; 849 } 850 851 static ssize_t ws_enc_write_payload(struct ws_encoder *enc, 852 struct Curl_easy *data, 853 const unsigned char *buf, size_t buflen, 854 struct bufq *out, CURLcode *err) 855 { 856 size_t i, len, n; 857 858 if(Curl_bufq_is_full(out)) { 859 *err = CURLE_AGAIN; 860 return -1; 861 } 862 863 /* not the most performant way to do this */ 864 len = buflen; 865 if((curl_off_t)len > enc->payload_remain) 866 len = (size_t)enc->payload_remain; 867 868 for(i = 0; i < len; ++i) { 869 unsigned char c = buf[i] ^ enc->mask[enc->xori]; 870 *err = Curl_bufq_write(out, &c, 1, &n); 871 if(*err) { 872 if((*err != CURLE_AGAIN) || !i) 873 return -1; 874 break; 875 } 876 enc->xori++; 877 enc->xori &= 3; 878 } 879 enc->payload_remain -= (curl_off_t)i; 880 ws_enc_info(enc, data, "buffered"); 881 return (ssize_t)i; 882 } 883 884 885 struct wsfield { 886 const char *name; 887 const char *val; 888 }; 889 890 CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req) 891 { 892 unsigned int i; 893 CURLcode result = CURLE_OK; 894 unsigned char rand[16]; 895 char *randstr; 896 size_t randlen; 897 char keyval[40]; 898 struct SingleRequest *k = &data->req; 899 struct wsfield heads[]= { 900 { 901 /* The request MUST contain an |Upgrade| header field whose value 902 MUST include the "websocket" keyword. */ 903 "Upgrade", "websocket" 904 }, 905 { 906 /* The request MUST contain a |Connection| header field whose value 907 MUST include the "Upgrade" token. */ 908 "Connection", "Upgrade", 909 }, 910 { 911 /* The request MUST include a header field with the name 912 |Sec-WebSocket-Version|. The value of this header field MUST be 913 13. */ 914 "Sec-WebSocket-Version", "13", 915 }, 916 { 917 /* The request MUST include a header field with the name 918 |Sec-WebSocket-Key|. The value of this header field MUST be a nonce 919 consisting of a randomly selected 16-byte value that has been 920 base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be 921 selected randomly for each connection. */ 922 "Sec-WebSocket-Key", NULL, 923 } 924 }; 925 heads[3].val = &keyval[0]; 926 927 /* 16 bytes random */ 928 result = Curl_rand(data, (unsigned char *)rand, sizeof(rand)); 929 if(result) 930 return result; 931 result = curlx_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen); 932 if(result) 933 return result; 934 DEBUGASSERT(randlen < sizeof(keyval)); 935 if(randlen >= sizeof(keyval)) { 936 free(randstr); 937 return CURLE_FAILED_INIT; 938 } 939 strcpy(keyval, randstr); 940 free(randstr); 941 for(i = 0; !result && (i < CURL_ARRAYSIZE(heads)); i++) { 942 if(!Curl_checkheaders(data, heads[i].name, strlen(heads[i].name))) { 943 result = curlx_dyn_addf(req, "%s: %s\r\n", heads[i].name, 944 heads[i].val); 945 } 946 } 947 k->upgr101 = UPGR101_WS; 948 return result; 949 } 950 951 static void ws_conn_dtor(void *key, size_t klen, void *entry) 952 { 953 struct websocket *ws = entry; 954 (void)key; 955 (void)klen; 956 Curl_bufq_free(&ws->recvbuf); 957 Curl_bufq_free(&ws->sendbuf); 958 free(ws); 959 } 960 961 /* 962 * 'nread' is number of bytes of websocket data already in the buffer at 963 * 'mem'. 964 */ 965 CURLcode Curl_ws_accept(struct Curl_easy *data, 966 const char *mem, size_t nread) 967 { 968 struct SingleRequest *k = &data->req; 969 struct websocket *ws; 970 struct Curl_cwriter *ws_dec_writer; 971 CURLcode result; 972 973 DEBUGASSERT(data->conn); 974 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); 975 if(!ws) { 976 size_t chunk_size = WS_CHUNK_SIZE; 977 ws = calloc(1, sizeof(*ws)); 978 if(!ws) 979 return CURLE_OUT_OF_MEMORY; 980 #ifdef DEBUGBUILD 981 { 982 const char *p = getenv("CURL_WS_CHUNK_SIZE"); 983 if(p) { 984 curl_off_t l; 985 if(!curlx_str_number(&p, &l, 1*1024*1024)) 986 chunk_size = (size_t)l; 987 } 988 } 989 #endif 990 CURL_TRC_WS(data, "WS, using chunk size %zu", chunk_size); 991 Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT, 992 BUFQ_OPT_SOFT_LIMIT); 993 Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT, 994 BUFQ_OPT_SOFT_LIMIT); 995 ws_dec_init(&ws->dec); 996 ws_enc_init(&ws->enc); 997 result = Curl_conn_meta_set(data->conn, CURL_META_PROTO_WS_CONN, 998 ws, ws_conn_dtor); 999 if(result) 1000 return result; 1001 } 1002 else { 1003 Curl_bufq_reset(&ws->recvbuf); 1004 ws_dec_reset(&ws->dec); 1005 ws_enc_reset(&ws->enc); 1006 } 1007 /* Verify the Sec-WebSocket-Accept response. 1008 1009 The sent value is the base64 encoded version of a SHA-1 hash done on the 1010 |Sec-WebSocket-Key| header field concatenated with 1011 the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11". 1012 */ 1013 1014 /* If the response includes a |Sec-WebSocket-Extensions| header field and 1015 this header field indicates the use of an extension that was not present 1016 in the client's handshake (the server has indicated an extension not 1017 requested by the client), the client MUST Fail the WebSocket Connection. 1018 */ 1019 1020 /* If the response includes a |Sec-WebSocket-Protocol| header field 1021 and this header field indicates the use of a subprotocol that was 1022 not present in the client's handshake (the server has indicated a 1023 subprotocol not requested by the client), the client MUST Fail 1024 the WebSocket Connection. */ 1025 1026 /* 4 bytes random */ 1027 1028 result = Curl_rand(data, (unsigned char *)&ws->enc.mask, 1029 sizeof(ws->enc.mask)); 1030 if(result) 1031 return result; 1032 1033 #ifdef DEBUGBUILD 1034 if(getenv("CURL_WS_FORCE_ZERO_MASK")) 1035 /* force the bit mask to 0x00000000, effectively disabling masking */ 1036 memset(ws->enc.mask, 0, sizeof(ws->enc.mask)); 1037 #endif 1038 1039 infof(data, "[WS] Received 101, switch to WebSocket; mask %02x%02x%02x%02x", 1040 ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]); 1041 1042 /* Install our client writer that decodes WS frames payload */ 1043 result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode, 1044 CURL_CW_CONTENT_DECODE); 1045 if(result) 1046 return result; 1047 1048 result = Curl_cwriter_add(data, ws_dec_writer); 1049 if(result) { 1050 Curl_cwriter_free(data, ws_dec_writer); 1051 return result; 1052 } 1053 1054 if(data->set.connect_only) { 1055 size_t nwritten; 1056 /* In CONNECT_ONLY setup, the payloads from `mem` need to be received 1057 * when using `curl_ws_recv` later on after this transfer is already 1058 * marked as DONE. */ 1059 result = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem, 1060 nread, &nwritten); 1061 if(result) 1062 return result; 1063 DEBUGASSERT(nread == nwritten); 1064 infof(data, "%zu bytes websocket payload", nread); 1065 } 1066 else { /* !connect_only */ 1067 /* And pass any additional data to the writers */ 1068 if(nread) { 1069 result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread); 1070 } 1071 } 1072 k->upgr101 = UPGR101_RECEIVED; 1073 1074 return result; 1075 } 1076 1077 struct ws_collect { 1078 struct Curl_easy *data; 1079 unsigned char *buffer; 1080 size_t buflen; 1081 size_t bufidx; 1082 int frame_age; 1083 int frame_flags; 1084 curl_off_t payload_offset; 1085 curl_off_t payload_len; 1086 bool written; 1087 }; 1088 1089 static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, 1090 int frame_age, int frame_flags, 1091 curl_off_t payload_offset, 1092 curl_off_t payload_len, 1093 void *userp, 1094 CURLcode *err) 1095 { 1096 struct ws_collect *ctx = userp; 1097 struct Curl_easy *data = ctx->data; 1098 bool auto_pong = !data->set.ws_no_auto_pong; 1099 size_t nwritten; 1100 curl_off_t remain = (payload_len - (payload_offset + buflen)); 1101 1102 if(!ctx->bufidx) { 1103 /* first write */ 1104 ctx->frame_age = frame_age; 1105 ctx->frame_flags = frame_flags; 1106 ctx->payload_offset = payload_offset; 1107 ctx->payload_len = payload_len; 1108 } 1109 1110 if(auto_pong && (frame_flags & CURLWS_PING) && !remain) { 1111 /* auto-respond to PINGs, only works for single-frame payloads atm */ 1112 size_t bytes; 1113 infof(ctx->data, "[WS] auto-respond to PING with a PONG"); 1114 /* send back the exact same content as a PONG */ 1115 *err = curl_ws_send(ctx->data, buf, buflen, &bytes, 0, CURLWS_PONG); 1116 if(*err) 1117 return -1; 1118 nwritten = bytes; 1119 } 1120 else { 1121 ctx->written = TRUE; 1122 DEBUGASSERT(ctx->buflen >= ctx->bufidx); 1123 nwritten = CURLMIN(buflen, ctx->buflen - ctx->bufidx); 1124 if(!nwritten) { 1125 if(!buflen) { /* 0 length write, we accept that */ 1126 *err = CURLE_OK; 1127 return 0; 1128 } 1129 *err = CURLE_AGAIN; /* no more space */ 1130 return -1; 1131 } 1132 *err = CURLE_OK; 1133 memcpy(ctx->buffer + ctx->bufidx, buf, nwritten); 1134 ctx->bufidx += nwritten; 1135 } 1136 return nwritten; 1137 } 1138 1139 static CURLcode nw_in_recv(void *reader_ctx, 1140 unsigned char *buf, size_t buflen, 1141 size_t *pnread) 1142 { 1143 struct Curl_easy *data = reader_ctx; 1144 return curl_easy_recv(data, buf, buflen, pnread); 1145 } 1146 1147 CURLcode curl_ws_recv(CURL *d, void *buffer, 1148 size_t buflen, size_t *nread, 1149 const struct curl_ws_frame **metap) 1150 { 1151 struct Curl_easy *data = d; 1152 struct connectdata *conn = data->conn; 1153 struct websocket *ws; 1154 struct ws_collect ctx; 1155 1156 *nread = 0; 1157 *metap = NULL; 1158 1159 if(!conn) { 1160 /* Unhappy hack with lifetimes of transfers and connection */ 1161 if(!data->set.connect_only) { 1162 failf(data, "[WS] CONNECT_ONLY is required"); 1163 return CURLE_UNSUPPORTED_PROTOCOL; 1164 } 1165 1166 Curl_getconnectinfo(data, &conn); 1167 if(!conn) { 1168 failf(data, "[WS] connection not found"); 1169 return CURLE_BAD_FUNCTION_ARGUMENT; 1170 } 1171 } 1172 ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN); 1173 if(!ws) { 1174 failf(data, "[WS] connection is not setup for websocket"); 1175 return CURLE_BAD_FUNCTION_ARGUMENT; 1176 } 1177 1178 1179 memset(&ctx, 0, sizeof(ctx)); 1180 ctx.data = data; 1181 ctx.buffer = buffer; 1182 ctx.buflen = buflen; 1183 1184 while(1) { 1185 CURLcode result; 1186 1187 /* receive more when our buffer is empty */ 1188 if(Curl_bufq_is_empty(&ws->recvbuf)) { 1189 size_t n; 1190 result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n); 1191 if(result) 1192 return result; 1193 else if(n == 0) { 1194 /* connection closed */ 1195 infof(data, "[WS] connection expectedly closed?"); 1196 return CURLE_GOT_NOTHING; 1197 } 1198 CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network", 1199 Curl_bufq_len(&ws->recvbuf)); 1200 } 1201 1202 result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, 1203 ws_client_collect, &ctx); 1204 if(result == CURLE_AGAIN) { 1205 if(!ctx.written) { 1206 ws_dec_info(&ws->dec, data, "need more input"); 1207 continue; /* nothing written, try more input */ 1208 } 1209 break; 1210 } 1211 else if(result) { 1212 return result; 1213 } 1214 else if(ctx.written) { 1215 /* The decoded frame is passed back to our caller. 1216 * There are frames like PING were we auto-respond to and 1217 * that we do not return. For these `ctx.written` is not set. */ 1218 break; 1219 } 1220 } 1221 1222 /* update frame information to be passed back */ 1223 update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, 1224 ctx.payload_len, ctx.bufidx); 1225 *metap = &ws->frame; 1226 *nread = ws->frame.len; 1227 CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" 1228 FMT_OFF_T ", %" FMT_OFF_T " left)", 1229 buflen, *nread, ws->frame.offset, ws->frame.bytesleft); 1230 return CURLE_OK; 1231 } 1232 1233 static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, 1234 bool blocking) 1235 { 1236 if(!Curl_bufq_is_empty(&ws->sendbuf)) { 1237 CURLcode result; 1238 const unsigned char *out; 1239 size_t outlen, n; 1240 #ifdef DEBUGBUILD 1241 /* Simulate a blocking send after this chunk has been sent */ 1242 bool eagain_next = FALSE; 1243 size_t chunk_egain = 0; 1244 const char *p = getenv("CURL_WS_CHUNK_EAGAIN"); 1245 if(p) { 1246 curl_off_t l; 1247 if(!curlx_str_number(&p, &l, 1*1024*1024)) 1248 chunk_egain = (size_t)l; 1249 } 1250 #endif 1251 1252 while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) { 1253 #ifdef DEBUGBUILD 1254 if(eagain_next) 1255 return CURLE_AGAIN; 1256 if(chunk_egain && (outlen > chunk_egain)) { 1257 outlen = chunk_egain; 1258 eagain_next = TRUE; 1259 } 1260 #endif 1261 if(blocking) { 1262 result = ws_send_raw_blocking(data, ws, (const char *)out, outlen); 1263 n = result ? 0 : outlen; 1264 } 1265 else if(data->set.connect_only || Curl_is_in_callback(data)) 1266 result = Curl_senddata(data, out, outlen, &n); 1267 else { 1268 result = Curl_xfer_send(data, out, outlen, FALSE, &n); 1269 if(!result && !n && outlen) 1270 result = CURLE_AGAIN; 1271 } 1272 1273 if(result == CURLE_AGAIN) { 1274 CURL_TRC_WS(data, "flush EAGAIN, %zu bytes remain in buffer", 1275 Curl_bufq_len(&ws->sendbuf)); 1276 return result; 1277 } 1278 else if(result) { 1279 failf(data, "[WS] flush, write error %d", result); 1280 return result; 1281 } 1282 else { 1283 CURL_TRC_WS(data, "flushed %zu bytes", n); 1284 Curl_bufq_skip(&ws->sendbuf, n); 1285 } 1286 } 1287 } 1288 return CURLE_OK; 1289 } 1290 1291 static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws, 1292 const char *buffer, size_t buflen) 1293 { 1294 CURLcode result = CURLE_OK; 1295 size_t nwritten; 1296 struct Curl_easy *data = d; 1297 1298 (void)ws; 1299 while(buflen) { 1300 result = Curl_xfer_send(data, buffer, buflen, FALSE, &nwritten); 1301 if(result) 1302 return result; 1303 DEBUGASSERT(nwritten <= buflen); 1304 buffer += nwritten; 1305 buflen -= nwritten; 1306 if(buflen) { 1307 curl_socket_t sock = data->conn->sock[FIRSTSOCKET]; 1308 timediff_t left_ms; 1309 int ev; 1310 1311 CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send", 1312 buflen); 1313 left_ms = Curl_timeleft(data, NULL, FALSE); 1314 if(left_ms < 0) { 1315 failf(data, "[WS] Timeout waiting for socket becoming writable"); 1316 return CURLE_SEND_ERROR; 1317 } 1318 1319 /* POLLOUT socket */ 1320 if(sock == CURL_SOCKET_BAD) 1321 return CURLE_SEND_ERROR; 1322 ev = Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, sock, 1323 left_ms ? left_ms : 500); 1324 if(ev < 0) { 1325 failf(data, "[WS] Error while waiting for socket becoming writable"); 1326 return CURLE_SEND_ERROR; 1327 } 1328 } 1329 } 1330 return result; 1331 } 1332 1333 static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, 1334 size_t buflen, size_t *pnwritten) 1335 { 1336 struct websocket *ws; 1337 CURLcode result; 1338 1339 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); 1340 if(!ws) { 1341 failf(data, "[WS] Not a websocket transfer"); 1342 return CURLE_SEND_ERROR; 1343 } 1344 if(!buflen) 1345 return CURLE_OK; 1346 1347 if(Curl_is_in_callback(data)) { 1348 /* When invoked from inside callbacks, we do a blocking send as the 1349 * callback will probably not implement partial writes that may then 1350 * mess up the ws framing subsequently. 1351 * We need any pending data to be flushed before sending. */ 1352 result = ws_flush(data, ws, TRUE); 1353 if(result) 1354 return result; 1355 result = ws_send_raw_blocking(data, ws, buffer, buflen); 1356 } 1357 else { 1358 /* We need any pending data to be sent or EAGAIN this call. */ 1359 result = ws_flush(data, ws, FALSE); 1360 if(result) 1361 return result; 1362 result = Curl_senddata(data, buffer, buflen, pnwritten); 1363 } 1364 1365 CURL_TRC_WS(data, "ws_send_raw(len=%zu) -> %d, %zu", 1366 buflen, result, *pnwritten); 1367 return result; 1368 } 1369 1370 CURLcode curl_ws_send(CURL *d, const void *buffer_arg, 1371 size_t buflen, size_t *sent, 1372 curl_off_t fragsize, 1373 unsigned int flags) 1374 { 1375 struct websocket *ws; 1376 const unsigned char *buffer = buffer_arg; 1377 ssize_t n; 1378 CURLcode result = CURLE_OK; 1379 struct Curl_easy *data = d; 1380 1381 CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T 1382 ", flags=%x), raw=%d", 1383 buflen, fragsize, flags, data->set.ws_raw_mode); 1384 *sent = 0; 1385 if(!data->conn && data->set.connect_only) { 1386 result = Curl_connect_only_attach(data); 1387 if(result) 1388 goto out; 1389 } 1390 if(!data->conn) { 1391 failf(data, "[WS] No associated connection"); 1392 result = CURLE_SEND_ERROR; 1393 goto out; 1394 } 1395 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); 1396 if(!ws) { 1397 failf(data, "[WS] Not a websocket transfer"); 1398 result = CURLE_SEND_ERROR; 1399 goto out; 1400 } 1401 1402 if(data->set.ws_raw_mode) { 1403 /* In raw mode, we write directly to the connection */ 1404 /* try flushing any content still waiting to be sent. */ 1405 result = ws_flush(data, ws, FALSE); 1406 if(result) 1407 goto out; 1408 1409 if(fragsize || flags) { 1410 failf(data, "[WS] fragsize and flags must be zero in raw mode"); 1411 return CURLE_BAD_FUNCTION_ARGUMENT; 1412 } 1413 result = ws_send_raw(data, buffer, buflen, sent); 1414 goto out; 1415 } 1416 1417 /* Not RAW mode, buf we do the frame encoding */ 1418 1419 if(ws->enc.payload_remain || !Curl_bufq_is_empty(&ws->sendbuf)) { 1420 /* a frame is ongoing with payload buffered or more payload 1421 * that needs to be encoded into the buffer */ 1422 if(buflen < ws->sendbuf_payload) { 1423 /* We have been called with LESS buffer data than before. This 1424 * is not how it's supposed too work. */ 1425 failf(data, "[WS] curl_ws_send() called with smaller 'buflen' than " 1426 "bytes already buffered in previous call, %zu vs %zu", 1427 buflen, ws->sendbuf_payload); 1428 result = CURLE_BAD_FUNCTION_ARGUMENT; 1429 goto out; 1430 } 1431 if((curl_off_t)buflen > 1432 (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) { 1433 /* too large buflen beyond payload length of frame */ 1434 failf(data, "[WS] unaligned frame size (sending %zu instead of %" 1435 FMT_OFF_T ")", 1436 buflen, ws->enc.payload_remain + ws->sendbuf_payload); 1437 result = CURLE_BAD_FUNCTION_ARGUMENT; 1438 goto out; 1439 } 1440 } 1441 else { 1442 /* starting a new frame, we want a clean sendbuf */ 1443 curl_off_t payload_len = (flags & CURLWS_OFFSET) ? 1444 fragsize : (curl_off_t)buflen; 1445 result = ws_flush(data, ws, Curl_is_in_callback(data)); 1446 if(result) 1447 goto out; 1448 1449 n = ws_enc_write_head(data, &ws->enc, flags, payload_len, 1450 &ws->sendbuf, &result); 1451 if(n < 0) 1452 goto out; 1453 } 1454 1455 /* While there is either sendbuf to flush OR more payload to encode... */ 1456 while(!Curl_bufq_is_empty(&ws->sendbuf) || (buflen > ws->sendbuf_payload)) { 1457 /* Try to add more payload to sendbuf */ 1458 if(buflen > ws->sendbuf_payload) { 1459 size_t prev_len = Curl_bufq_len(&ws->sendbuf); 1460 n = ws_enc_write_payload(&ws->enc, data, 1461 buffer + ws->sendbuf_payload, 1462 buflen - ws->sendbuf_payload, 1463 &ws->sendbuf, &result); 1464 if(n < 0 && (result != CURLE_AGAIN)) 1465 goto out; 1466 ws->sendbuf_payload += Curl_bufq_len(&ws->sendbuf) - prev_len; 1467 if(!ws->sendbuf_payload) { 1468 result = CURLE_AGAIN; 1469 goto out; 1470 } 1471 } 1472 1473 /* flush, blocking when in callback */ 1474 result = ws_flush(data, ws, Curl_is_in_callback(data)); 1475 if(!result && ws->sendbuf_payload > 0) { 1476 *sent += ws->sendbuf_payload; 1477 buffer += ws->sendbuf_payload; 1478 buflen -= ws->sendbuf_payload; 1479 ws->sendbuf_payload = 0; 1480 } 1481 else if(result == CURLE_AGAIN) { 1482 if(ws->sendbuf_payload > Curl_bufq_len(&ws->sendbuf)) { 1483 /* blocked, part of payload bytes remain, report length 1484 * that we managed to send. */ 1485 size_t flushed = (ws->sendbuf_payload - Curl_bufq_len(&ws->sendbuf)); 1486 *sent += flushed; 1487 ws->sendbuf_payload -= flushed; 1488 result = CURLE_OK; 1489 goto out; 1490 } 1491 else { 1492 /* blocked before sending headers or 1st payload byte. We cannot report 1493 * OK on 0-length send (caller counts only payload) and EAGAIN */ 1494 CURL_TRC_WS(data, "EAGAIN flushing sendbuf, payload_encoded: %zu/%zu", 1495 ws->sendbuf_payload, buflen); 1496 DEBUGASSERT(*sent == 0); 1497 result = CURLE_AGAIN; 1498 goto out; 1499 } 1500 } 1501 else 1502 goto out; /* real error sending the data */ 1503 } 1504 1505 out: 1506 CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T 1507 ", flags=%x, raw=%d) -> %d, %zu", 1508 buflen, fragsize, flags, data->set.ws_raw_mode, result, *sent); 1509 return result; 1510 } 1511 1512 static CURLcode ws_setup_conn(struct Curl_easy *data, 1513 struct connectdata *conn) 1514 { 1515 /* WebSockets is 1.1 only (for now) */ 1516 data->state.http_neg.accept_09 = FALSE; 1517 data->state.http_neg.only_10 = FALSE; 1518 data->state.http_neg.wanted = CURL_HTTP_V1x; 1519 data->state.http_neg.allowed = CURL_HTTP_V1x; 1520 return Curl_http_setup_conn(data, conn); 1521 } 1522 1523 1524 const struct curl_ws_frame *curl_ws_meta(CURL *d) 1525 { 1526 /* we only return something for websocket, called from within the callback 1527 when not using raw mode */ 1528 struct Curl_easy *data = d; 1529 if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && 1530 data->conn && !data->set.ws_raw_mode) { 1531 struct websocket *ws; 1532 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); 1533 if(ws) 1534 return &ws->frame; 1535 1536 } 1537 return NULL; 1538 } 1539 1540 const struct Curl_handler Curl_handler_ws = { 1541 "WS", /* scheme */ 1542 ws_setup_conn, /* setup_connection */ 1543 Curl_http, /* do_it */ 1544 Curl_http_done, /* done */ 1545 ZERO_NULL, /* do_more */ 1546 Curl_http_connect, /* connect_it */ 1547 ZERO_NULL, /* connecting */ 1548 ZERO_NULL, /* doing */ 1549 ZERO_NULL, /* proto_getsock */ 1550 Curl_http_getsock_do, /* doing_getsock */ 1551 ZERO_NULL, /* domore_getsock */ 1552 ZERO_NULL, /* perform_getsock */ 1553 ZERO_NULL, /* disconnect */ 1554 Curl_http_write_resp, /* write_resp */ 1555 Curl_http_write_resp_hd, /* write_resp_hd */ 1556 ZERO_NULL, /* connection_check */ 1557 ZERO_NULL, /* attach connection */ 1558 Curl_http_follow, /* follow */ 1559 PORT_HTTP, /* defport */ 1560 CURLPROTO_WS, /* protocol */ 1561 CURLPROTO_HTTP, /* family */ 1562 PROTOPT_CREDSPERREQUEST | /* flags */ 1563 PROTOPT_USERPWDCTRL 1564 }; 1565 1566 #ifdef USE_SSL 1567 const struct Curl_handler Curl_handler_wss = { 1568 "WSS", /* scheme */ 1569 ws_setup_conn, /* setup_connection */ 1570 Curl_http, /* do_it */ 1571 Curl_http_done, /* done */ 1572 ZERO_NULL, /* do_more */ 1573 Curl_http_connect, /* connect_it */ 1574 NULL, /* connecting */ 1575 ZERO_NULL, /* doing */ 1576 NULL, /* proto_getsock */ 1577 Curl_http_getsock_do, /* doing_getsock */ 1578 ZERO_NULL, /* domore_getsock */ 1579 ZERO_NULL, /* perform_getsock */ 1580 ZERO_NULL, /* disconnect */ 1581 Curl_http_write_resp, /* write_resp */ 1582 Curl_http_write_resp_hd, /* write_resp_hd */ 1583 ZERO_NULL, /* connection_check */ 1584 ZERO_NULL, /* attach connection */ 1585 Curl_http_follow, /* follow */ 1586 PORT_HTTPS, /* defport */ 1587 CURLPROTO_WSS, /* protocol */ 1588 CURLPROTO_HTTP, /* family */ 1589 PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ 1590 PROTOPT_USERPWDCTRL 1591 }; 1592 #endif 1593 1594 1595 #else 1596 1597 CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, 1598 size_t *nread, 1599 const struct curl_ws_frame **metap) 1600 { 1601 (void)curl; 1602 (void)buffer; 1603 (void)buflen; 1604 (void)nread; 1605 (void)metap; 1606 return CURLE_NOT_BUILT_IN; 1607 } 1608 1609 CURLcode curl_ws_send(CURL *curl, const void *buffer, 1610 size_t buflen, size_t *sent, 1611 curl_off_t fragsize, 1612 unsigned int flags) 1613 { 1614 (void)curl; 1615 (void)buffer; 1616 (void)buflen; 1617 (void)sent; 1618 (void)fragsize; 1619 (void)flags; 1620 return CURLE_NOT_BUILT_IN; 1621 } 1622 1623 const struct curl_ws_frame *curl_ws_meta(CURL *data) 1624 { 1625 (void)data; 1626 return NULL; 1627 } 1628 #endif /* !CURL_DISABLE_WEBSOCKETS */