quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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 */