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