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