VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/ws.c

最後變更 在這個檔案是 104083,由 vboxsync 提交於 11 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette