113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci#include "curl_setup.h" 2513498266Sopenharmony_ci#include <curl/curl.h> 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include "urldata.h" 3013498266Sopenharmony_ci#include "bufq.h" 3113498266Sopenharmony_ci#include "dynbuf.h" 3213498266Sopenharmony_ci#include "rand.h" 3313498266Sopenharmony_ci#include "curl_base64.h" 3413498266Sopenharmony_ci#include "connect.h" 3513498266Sopenharmony_ci#include "sendf.h" 3613498266Sopenharmony_ci#include "multiif.h" 3713498266Sopenharmony_ci#include "ws.h" 3813498266Sopenharmony_ci#include "easyif.h" 3913498266Sopenharmony_ci#include "transfer.h" 4013498266Sopenharmony_ci#include "nonblock.h" 4113498266Sopenharmony_ci 4213498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 4313498266Sopenharmony_ci#include "curl_printf.h" 4413498266Sopenharmony_ci#include "curl_memory.h" 4513498266Sopenharmony_ci#include "memdebug.h" 4613498266Sopenharmony_ci 4713498266Sopenharmony_ci 4813498266Sopenharmony_ci#define WSBIT_FIN 0x80 4913498266Sopenharmony_ci#define WSBIT_OPCODE_CONT 0 5013498266Sopenharmony_ci#define WSBIT_OPCODE_TEXT (1) 5113498266Sopenharmony_ci#define WSBIT_OPCODE_BIN (2) 5213498266Sopenharmony_ci#define WSBIT_OPCODE_CLOSE (8) 5313498266Sopenharmony_ci#define WSBIT_OPCODE_PING (9) 5413498266Sopenharmony_ci#define WSBIT_OPCODE_PONG (0xa) 5513498266Sopenharmony_ci#define WSBIT_OPCODE_MASK (0xf) 5613498266Sopenharmony_ci 5713498266Sopenharmony_ci#define WSBIT_MASK 0x80 5813498266Sopenharmony_ci 5913498266Sopenharmony_ci/* buffer dimensioning */ 6013498266Sopenharmony_ci#define WS_CHUNK_SIZE 65535 6113498266Sopenharmony_ci#define WS_CHUNK_COUNT 2 6213498266Sopenharmony_ci 6313498266Sopenharmony_cistruct ws_frame_meta { 6413498266Sopenharmony_ci char proto_opcode; 6513498266Sopenharmony_ci int flags; 6613498266Sopenharmony_ci const char *name; 6713498266Sopenharmony_ci}; 6813498266Sopenharmony_ci 6913498266Sopenharmony_cistatic struct ws_frame_meta WS_FRAMES[] = { 7013498266Sopenharmony_ci { WSBIT_OPCODE_CONT, CURLWS_CONT, "CONT" }, 7113498266Sopenharmony_ci { WSBIT_OPCODE_TEXT, CURLWS_TEXT, "TEXT" }, 7213498266Sopenharmony_ci { WSBIT_OPCODE_BIN, CURLWS_BINARY, "BIN" }, 7313498266Sopenharmony_ci { WSBIT_OPCODE_CLOSE, CURLWS_CLOSE, "CLOSE" }, 7413498266Sopenharmony_ci { WSBIT_OPCODE_PING, CURLWS_PING, "PING" }, 7513498266Sopenharmony_ci { WSBIT_OPCODE_PONG, CURLWS_PONG, "PONG" }, 7613498266Sopenharmony_ci}; 7713498266Sopenharmony_ci 7813498266Sopenharmony_cistatic const char *ws_frame_name_of_op(unsigned char proto_opcode) 7913498266Sopenharmony_ci{ 8013498266Sopenharmony_ci unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK; 8113498266Sopenharmony_ci size_t i; 8213498266Sopenharmony_ci for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { 8313498266Sopenharmony_ci if(WS_FRAMES[i].proto_opcode == opcode) 8413498266Sopenharmony_ci return WS_FRAMES[i].name; 8513498266Sopenharmony_ci } 8613498266Sopenharmony_ci return "???"; 8713498266Sopenharmony_ci} 8813498266Sopenharmony_ci 8913498266Sopenharmony_cistatic int ws_frame_op2flags(unsigned char proto_opcode) 9013498266Sopenharmony_ci{ 9113498266Sopenharmony_ci unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK; 9213498266Sopenharmony_ci size_t i; 9313498266Sopenharmony_ci for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { 9413498266Sopenharmony_ci if(WS_FRAMES[i].proto_opcode == opcode) 9513498266Sopenharmony_ci return WS_FRAMES[i].flags; 9613498266Sopenharmony_ci } 9713498266Sopenharmony_ci return 0; 9813498266Sopenharmony_ci} 9913498266Sopenharmony_ci 10013498266Sopenharmony_cistatic unsigned char ws_frame_flags2op(int flags) 10113498266Sopenharmony_ci{ 10213498266Sopenharmony_ci size_t i; 10313498266Sopenharmony_ci for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { 10413498266Sopenharmony_ci if(WS_FRAMES[i].flags & flags) 10513498266Sopenharmony_ci return WS_FRAMES[i].proto_opcode; 10613498266Sopenharmony_ci } 10713498266Sopenharmony_ci return 0; 10813498266Sopenharmony_ci} 10913498266Sopenharmony_ci 11013498266Sopenharmony_cistatic void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, 11113498266Sopenharmony_ci const char *msg) 11213498266Sopenharmony_ci{ 11313498266Sopenharmony_ci switch(dec->head_len) { 11413498266Sopenharmony_ci case 0: 11513498266Sopenharmony_ci break; 11613498266Sopenharmony_ci case 1: 11713498266Sopenharmony_ci infof(data, "WS-DEC: %s [%s%s]", msg, 11813498266Sopenharmony_ci ws_frame_name_of_op(dec->head[0]), 11913498266Sopenharmony_ci (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL"); 12013498266Sopenharmony_ci break; 12113498266Sopenharmony_ci default: 12213498266Sopenharmony_ci if(dec->head_len < dec->head_total) { 12313498266Sopenharmony_ci infof(data, "WS-DEC: %s [%s%s](%d/%d)", msg, 12413498266Sopenharmony_ci ws_frame_name_of_op(dec->head[0]), 12513498266Sopenharmony_ci (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", 12613498266Sopenharmony_ci dec->head_len, dec->head_total); 12713498266Sopenharmony_ci } 12813498266Sopenharmony_ci else { 12913498266Sopenharmony_ci infof(data, "WS-DEC: %s [%s%s payload=%" CURL_FORMAT_CURL_OFF_T 13013498266Sopenharmony_ci "/%" CURL_FORMAT_CURL_OFF_T "]", 13113498266Sopenharmony_ci msg, ws_frame_name_of_op(dec->head[0]), 13213498266Sopenharmony_ci (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", 13313498266Sopenharmony_ci dec->payload_offset, dec->payload_len); 13413498266Sopenharmony_ci } 13513498266Sopenharmony_ci break; 13613498266Sopenharmony_ci } 13713498266Sopenharmony_ci} 13813498266Sopenharmony_ci 13913498266Sopenharmony_citypedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen, 14013498266Sopenharmony_ci int frame_age, int frame_flags, 14113498266Sopenharmony_ci curl_off_t payload_offset, 14213498266Sopenharmony_ci curl_off_t payload_len, 14313498266Sopenharmony_ci void *userp, 14413498266Sopenharmony_ci CURLcode *err); 14513498266Sopenharmony_ci 14613498266Sopenharmony_ci 14713498266Sopenharmony_cistatic void ws_dec_reset(struct ws_decoder *dec) 14813498266Sopenharmony_ci{ 14913498266Sopenharmony_ci dec->frame_age = 0; 15013498266Sopenharmony_ci dec->frame_flags = 0; 15113498266Sopenharmony_ci dec->payload_offset = 0; 15213498266Sopenharmony_ci dec->payload_len = 0; 15313498266Sopenharmony_ci dec->head_len = dec->head_total = 0; 15413498266Sopenharmony_ci dec->state = WS_DEC_INIT; 15513498266Sopenharmony_ci} 15613498266Sopenharmony_ci 15713498266Sopenharmony_cistatic void ws_dec_init(struct ws_decoder *dec) 15813498266Sopenharmony_ci{ 15913498266Sopenharmony_ci ws_dec_reset(dec); 16013498266Sopenharmony_ci} 16113498266Sopenharmony_ci 16213498266Sopenharmony_cistatic CURLcode ws_dec_read_head(struct ws_decoder *dec, 16313498266Sopenharmony_ci struct Curl_easy *data, 16413498266Sopenharmony_ci struct bufq *inraw) 16513498266Sopenharmony_ci{ 16613498266Sopenharmony_ci const unsigned char *inbuf; 16713498266Sopenharmony_ci size_t inlen; 16813498266Sopenharmony_ci 16913498266Sopenharmony_ci while(Curl_bufq_peek(inraw, &inbuf, &inlen)) { 17013498266Sopenharmony_ci if(dec->head_len == 0) { 17113498266Sopenharmony_ci dec->head[0] = *inbuf; 17213498266Sopenharmony_ci Curl_bufq_skip(inraw, 1); 17313498266Sopenharmony_ci 17413498266Sopenharmony_ci dec->frame_flags = ws_frame_op2flags(dec->head[0]); 17513498266Sopenharmony_ci if(!dec->frame_flags) { 17613498266Sopenharmony_ci failf(data, "WS: unknown opcode: %x", dec->head[0]); 17713498266Sopenharmony_ci ws_dec_reset(dec); 17813498266Sopenharmony_ci return CURLE_RECV_ERROR; 17913498266Sopenharmony_ci } 18013498266Sopenharmony_ci dec->head_len = 1; 18113498266Sopenharmony_ci /* ws_dec_info(dec, data, "seeing opcode"); */ 18213498266Sopenharmony_ci continue; 18313498266Sopenharmony_ci } 18413498266Sopenharmony_ci else if(dec->head_len == 1) { 18513498266Sopenharmony_ci dec->head[1] = *inbuf; 18613498266Sopenharmony_ci Curl_bufq_skip(inraw, 1); 18713498266Sopenharmony_ci dec->head_len = 2; 18813498266Sopenharmony_ci 18913498266Sopenharmony_ci if(dec->head[1] & WSBIT_MASK) { 19013498266Sopenharmony_ci /* A client MUST close a connection if it detects a masked frame. */ 19113498266Sopenharmony_ci failf(data, "WS: masked input frame"); 19213498266Sopenharmony_ci ws_dec_reset(dec); 19313498266Sopenharmony_ci return CURLE_RECV_ERROR; 19413498266Sopenharmony_ci } 19513498266Sopenharmony_ci /* How long is the frame head? */ 19613498266Sopenharmony_ci if(dec->head[1] == 126) { 19713498266Sopenharmony_ci dec->head_total = 4; 19813498266Sopenharmony_ci continue; 19913498266Sopenharmony_ci } 20013498266Sopenharmony_ci else if(dec->head[1] == 127) { 20113498266Sopenharmony_ci dec->head_total = 10; 20213498266Sopenharmony_ci continue; 20313498266Sopenharmony_ci } 20413498266Sopenharmony_ci else { 20513498266Sopenharmony_ci dec->head_total = 2; 20613498266Sopenharmony_ci } 20713498266Sopenharmony_ci } 20813498266Sopenharmony_ci 20913498266Sopenharmony_ci if(dec->head_len < dec->head_total) { 21013498266Sopenharmony_ci dec->head[dec->head_len] = *inbuf; 21113498266Sopenharmony_ci Curl_bufq_skip(inraw, 1); 21213498266Sopenharmony_ci ++dec->head_len; 21313498266Sopenharmony_ci if(dec->head_len < dec->head_total) { 21413498266Sopenharmony_ci /* ws_dec_info(dec, data, "decoding head"); */ 21513498266Sopenharmony_ci continue; 21613498266Sopenharmony_ci } 21713498266Sopenharmony_ci } 21813498266Sopenharmony_ci /* got the complete frame head */ 21913498266Sopenharmony_ci DEBUGASSERT(dec->head_len == dec->head_total); 22013498266Sopenharmony_ci switch(dec->head_total) { 22113498266Sopenharmony_ci case 2: 22213498266Sopenharmony_ci dec->payload_len = dec->head[1]; 22313498266Sopenharmony_ci break; 22413498266Sopenharmony_ci case 4: 22513498266Sopenharmony_ci dec->payload_len = (dec->head[2] << 8) | dec->head[3]; 22613498266Sopenharmony_ci break; 22713498266Sopenharmony_ci case 10: 22813498266Sopenharmony_ci if(dec->head[2] > 127) { 22913498266Sopenharmony_ci failf(data, "WS: frame length longer than 64 signed not supported"); 23013498266Sopenharmony_ci return CURLE_RECV_ERROR; 23113498266Sopenharmony_ci } 23213498266Sopenharmony_ci dec->payload_len = ((curl_off_t)dec->head[2] << 56) | 23313498266Sopenharmony_ci (curl_off_t)dec->head[3] << 48 | 23413498266Sopenharmony_ci (curl_off_t)dec->head[4] << 40 | 23513498266Sopenharmony_ci (curl_off_t)dec->head[5] << 32 | 23613498266Sopenharmony_ci (curl_off_t)dec->head[6] << 24 | 23713498266Sopenharmony_ci (curl_off_t)dec->head[7] << 16 | 23813498266Sopenharmony_ci (curl_off_t)dec->head[8] << 8 | 23913498266Sopenharmony_ci dec->head[9]; 24013498266Sopenharmony_ci break; 24113498266Sopenharmony_ci default: 24213498266Sopenharmony_ci /* this should never happen */ 24313498266Sopenharmony_ci DEBUGASSERT(0); 24413498266Sopenharmony_ci failf(data, "WS: unexpected frame header length"); 24513498266Sopenharmony_ci return CURLE_RECV_ERROR; 24613498266Sopenharmony_ci } 24713498266Sopenharmony_ci 24813498266Sopenharmony_ci dec->frame_age = 0; 24913498266Sopenharmony_ci dec->payload_offset = 0; 25013498266Sopenharmony_ci ws_dec_info(dec, data, "decoded"); 25113498266Sopenharmony_ci return CURLE_OK; 25213498266Sopenharmony_ci } 25313498266Sopenharmony_ci return CURLE_AGAIN; 25413498266Sopenharmony_ci} 25513498266Sopenharmony_ci 25613498266Sopenharmony_cistatic CURLcode ws_dec_pass_payload(struct ws_decoder *dec, 25713498266Sopenharmony_ci struct Curl_easy *data, 25813498266Sopenharmony_ci struct bufq *inraw, 25913498266Sopenharmony_ci ws_write_payload *write_payload, 26013498266Sopenharmony_ci void *write_ctx) 26113498266Sopenharmony_ci{ 26213498266Sopenharmony_ci const unsigned char *inbuf; 26313498266Sopenharmony_ci size_t inlen; 26413498266Sopenharmony_ci ssize_t nwritten; 26513498266Sopenharmony_ci CURLcode result; 26613498266Sopenharmony_ci curl_off_t remain = dec->payload_len - dec->payload_offset; 26713498266Sopenharmony_ci 26813498266Sopenharmony_ci (void)data; 26913498266Sopenharmony_ci while(remain && Curl_bufq_peek(inraw, &inbuf, &inlen)) { 27013498266Sopenharmony_ci if((curl_off_t)inlen > remain) 27113498266Sopenharmony_ci inlen = (size_t)remain; 27213498266Sopenharmony_ci nwritten = write_payload(inbuf, inlen, dec->frame_age, dec->frame_flags, 27313498266Sopenharmony_ci dec->payload_offset, dec->payload_len, 27413498266Sopenharmony_ci write_ctx, &result); 27513498266Sopenharmony_ci if(nwritten < 0) 27613498266Sopenharmony_ci return result; 27713498266Sopenharmony_ci Curl_bufq_skip(inraw, (size_t)nwritten); 27813498266Sopenharmony_ci dec->payload_offset += (curl_off_t)nwritten; 27913498266Sopenharmony_ci remain = dec->payload_len - dec->payload_offset; 28013498266Sopenharmony_ci /* infof(data, "WS-DEC: passed %zd bytes payload, %" 28113498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T " remain", 28213498266Sopenharmony_ci nwritten, remain); */ 28313498266Sopenharmony_ci } 28413498266Sopenharmony_ci 28513498266Sopenharmony_ci return remain? CURLE_AGAIN : CURLE_OK; 28613498266Sopenharmony_ci} 28713498266Sopenharmony_ci 28813498266Sopenharmony_cistatic CURLcode ws_dec_pass(struct ws_decoder *dec, 28913498266Sopenharmony_ci struct Curl_easy *data, 29013498266Sopenharmony_ci struct bufq *inraw, 29113498266Sopenharmony_ci ws_write_payload *write_payload, 29213498266Sopenharmony_ci void *write_ctx) 29313498266Sopenharmony_ci{ 29413498266Sopenharmony_ci CURLcode result; 29513498266Sopenharmony_ci 29613498266Sopenharmony_ci if(Curl_bufq_is_empty(inraw)) 29713498266Sopenharmony_ci return CURLE_AGAIN; 29813498266Sopenharmony_ci 29913498266Sopenharmony_ci switch(dec->state) { 30013498266Sopenharmony_ci case WS_DEC_INIT: 30113498266Sopenharmony_ci ws_dec_reset(dec); 30213498266Sopenharmony_ci dec->state = WS_DEC_HEAD; 30313498266Sopenharmony_ci FALLTHROUGH(); 30413498266Sopenharmony_ci case WS_DEC_HEAD: 30513498266Sopenharmony_ci result = ws_dec_read_head(dec, data, inraw); 30613498266Sopenharmony_ci if(result) { 30713498266Sopenharmony_ci if(result != CURLE_AGAIN) { 30813498266Sopenharmony_ci infof(data, "WS: decode error %d", (int)result); 30913498266Sopenharmony_ci break; /* real error */ 31013498266Sopenharmony_ci } 31113498266Sopenharmony_ci /* incomplete ws frame head */ 31213498266Sopenharmony_ci DEBUGASSERT(Curl_bufq_is_empty(inraw)); 31313498266Sopenharmony_ci break; 31413498266Sopenharmony_ci } 31513498266Sopenharmony_ci /* head parsing done */ 31613498266Sopenharmony_ci dec->state = WS_DEC_PAYLOAD; 31713498266Sopenharmony_ci if(dec->payload_len == 0) { 31813498266Sopenharmony_ci ssize_t nwritten; 31913498266Sopenharmony_ci const unsigned char tmp = '\0'; 32013498266Sopenharmony_ci /* special case of a 0 length frame, need to write once */ 32113498266Sopenharmony_ci nwritten = write_payload(&tmp, 0, dec->frame_age, dec->frame_flags, 32213498266Sopenharmony_ci 0, 0, write_ctx, &result); 32313498266Sopenharmony_ci if(nwritten < 0) 32413498266Sopenharmony_ci return result; 32513498266Sopenharmony_ci dec->state = WS_DEC_INIT; 32613498266Sopenharmony_ci break; 32713498266Sopenharmony_ci } 32813498266Sopenharmony_ci FALLTHROUGH(); 32913498266Sopenharmony_ci case WS_DEC_PAYLOAD: 33013498266Sopenharmony_ci result = ws_dec_pass_payload(dec, data, inraw, write_payload, write_ctx); 33113498266Sopenharmony_ci ws_dec_info(dec, data, "passing"); 33213498266Sopenharmony_ci if(result) 33313498266Sopenharmony_ci return result; 33413498266Sopenharmony_ci /* paylod parsing done */ 33513498266Sopenharmony_ci dec->state = WS_DEC_INIT; 33613498266Sopenharmony_ci break; 33713498266Sopenharmony_ci default: 33813498266Sopenharmony_ci /* we covered all enums above, but some code analyzers are whimps */ 33913498266Sopenharmony_ci result = CURLE_FAILED_INIT; 34013498266Sopenharmony_ci } 34113498266Sopenharmony_ci return result; 34213498266Sopenharmony_ci} 34313498266Sopenharmony_ci 34413498266Sopenharmony_cistatic void update_meta(struct websocket *ws, 34513498266Sopenharmony_ci int frame_age, int frame_flags, 34613498266Sopenharmony_ci curl_off_t payload_offset, 34713498266Sopenharmony_ci curl_off_t payload_len, 34813498266Sopenharmony_ci size_t cur_len) 34913498266Sopenharmony_ci{ 35013498266Sopenharmony_ci ws->frame.age = frame_age; 35113498266Sopenharmony_ci ws->frame.flags = frame_flags; 35213498266Sopenharmony_ci ws->frame.offset = payload_offset; 35313498266Sopenharmony_ci ws->frame.len = cur_len; 35413498266Sopenharmony_ci ws->frame.bytesleft = (payload_len - payload_offset - cur_len); 35513498266Sopenharmony_ci} 35613498266Sopenharmony_ci 35713498266Sopenharmony_ci/* WebSockets decoding client writer */ 35813498266Sopenharmony_cistruct ws_cw_ctx { 35913498266Sopenharmony_ci struct Curl_cwriter super; 36013498266Sopenharmony_ci struct bufq buf; 36113498266Sopenharmony_ci}; 36213498266Sopenharmony_ci 36313498266Sopenharmony_cistatic CURLcode ws_cw_init(struct Curl_easy *data, 36413498266Sopenharmony_ci struct Curl_cwriter *writer) 36513498266Sopenharmony_ci{ 36613498266Sopenharmony_ci struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer; 36713498266Sopenharmony_ci (void)data; 36813498266Sopenharmony_ci Curl_bufq_init2(&ctx->buf, WS_CHUNK_SIZE, 1, BUFQ_OPT_SOFT_LIMIT); 36913498266Sopenharmony_ci return CURLE_OK; 37013498266Sopenharmony_ci} 37113498266Sopenharmony_ci 37213498266Sopenharmony_cistatic void ws_cw_close(struct Curl_easy *data, struct Curl_cwriter *writer) 37313498266Sopenharmony_ci{ 37413498266Sopenharmony_ci struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer; 37513498266Sopenharmony_ci (void) data; 37613498266Sopenharmony_ci Curl_bufq_free(&ctx->buf); 37713498266Sopenharmony_ci} 37813498266Sopenharmony_ci 37913498266Sopenharmony_cistruct ws_cw_dec_ctx { 38013498266Sopenharmony_ci struct Curl_easy *data; 38113498266Sopenharmony_ci struct websocket *ws; 38213498266Sopenharmony_ci struct Curl_cwriter *next_writer; 38313498266Sopenharmony_ci int cw_type; 38413498266Sopenharmony_ci}; 38513498266Sopenharmony_ci 38613498266Sopenharmony_cistatic ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen, 38713498266Sopenharmony_ci int frame_age, int frame_flags, 38813498266Sopenharmony_ci curl_off_t payload_offset, 38913498266Sopenharmony_ci curl_off_t payload_len, 39013498266Sopenharmony_ci void *user_data, 39113498266Sopenharmony_ci CURLcode *err) 39213498266Sopenharmony_ci{ 39313498266Sopenharmony_ci struct ws_cw_dec_ctx *ctx = user_data; 39413498266Sopenharmony_ci struct Curl_easy *data = ctx->data; 39513498266Sopenharmony_ci struct websocket *ws = ctx->ws; 39613498266Sopenharmony_ci curl_off_t remain = (payload_len - (payload_offset + buflen)); 39713498266Sopenharmony_ci 39813498266Sopenharmony_ci (void)frame_age; 39913498266Sopenharmony_ci if((frame_flags & CURLWS_PING) && !remain) { 40013498266Sopenharmony_ci /* auto-respond to PINGs, only works for single-frame payloads atm */ 40113498266Sopenharmony_ci size_t bytes; 40213498266Sopenharmony_ci infof(data, "WS: auto-respond to PING with a PONG"); 40313498266Sopenharmony_ci /* send back the exact same content as a PONG */ 40413498266Sopenharmony_ci *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG); 40513498266Sopenharmony_ci if(*err) 40613498266Sopenharmony_ci return -1; 40713498266Sopenharmony_ci } 40813498266Sopenharmony_ci else if(buflen || !remain) { 40913498266Sopenharmony_ci /* forward the decoded frame to the next client writer. */ 41013498266Sopenharmony_ci update_meta(ws, frame_age, frame_flags, payload_offset, 41113498266Sopenharmony_ci payload_len, buflen); 41213498266Sopenharmony_ci 41313498266Sopenharmony_ci *err = Curl_cwriter_write(data, ctx->next_writer, ctx->cw_type, 41413498266Sopenharmony_ci (const char *)buf, buflen); 41513498266Sopenharmony_ci if(*err) 41613498266Sopenharmony_ci return -1; 41713498266Sopenharmony_ci } 41813498266Sopenharmony_ci *err = CURLE_OK; 41913498266Sopenharmony_ci return (ssize_t)buflen; 42013498266Sopenharmony_ci} 42113498266Sopenharmony_ci 42213498266Sopenharmony_cistatic CURLcode ws_cw_write(struct Curl_easy *data, 42313498266Sopenharmony_ci struct Curl_cwriter *writer, int type, 42413498266Sopenharmony_ci const char *buf, size_t nbytes) 42513498266Sopenharmony_ci{ 42613498266Sopenharmony_ci struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer; 42713498266Sopenharmony_ci struct websocket *ws; 42813498266Sopenharmony_ci CURLcode result; 42913498266Sopenharmony_ci 43013498266Sopenharmony_ci if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode) 43113498266Sopenharmony_ci return Curl_cwriter_write(data, writer->next, type, buf, nbytes); 43213498266Sopenharmony_ci 43313498266Sopenharmony_ci ws = data->conn->proto.ws; 43413498266Sopenharmony_ci if(!ws) { 43513498266Sopenharmony_ci failf(data, "WS: not a websocket transfer"); 43613498266Sopenharmony_ci return CURLE_FAILED_INIT; 43713498266Sopenharmony_ci } 43813498266Sopenharmony_ci 43913498266Sopenharmony_ci if(nbytes) { 44013498266Sopenharmony_ci ssize_t nwritten; 44113498266Sopenharmony_ci nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf, 44213498266Sopenharmony_ci nbytes, &result); 44313498266Sopenharmony_ci if(nwritten < 0) { 44413498266Sopenharmony_ci infof(data, "WS: error adding data to buffer %d", result); 44513498266Sopenharmony_ci return result; 44613498266Sopenharmony_ci } 44713498266Sopenharmony_ci } 44813498266Sopenharmony_ci 44913498266Sopenharmony_ci while(!Curl_bufq_is_empty(&ctx->buf)) { 45013498266Sopenharmony_ci struct ws_cw_dec_ctx pass_ctx; 45113498266Sopenharmony_ci pass_ctx.data = data; 45213498266Sopenharmony_ci pass_ctx.ws = ws; 45313498266Sopenharmony_ci pass_ctx.next_writer = writer->next; 45413498266Sopenharmony_ci pass_ctx.cw_type = type; 45513498266Sopenharmony_ci result = ws_dec_pass(&ws->dec, data, &ctx->buf, 45613498266Sopenharmony_ci ws_cw_dec_next, &pass_ctx); 45713498266Sopenharmony_ci if(result == CURLE_AGAIN) 45813498266Sopenharmony_ci /* insufficient amount of data, keep it for later. 45913498266Sopenharmony_ci * we pretend to have written all since we have a copy */ 46013498266Sopenharmony_ci return CURLE_OK; 46113498266Sopenharmony_ci else if(result) { 46213498266Sopenharmony_ci infof(data, "WS: decode error %d", (int)result); 46313498266Sopenharmony_ci return result; 46413498266Sopenharmony_ci } 46513498266Sopenharmony_ci } 46613498266Sopenharmony_ci 46713498266Sopenharmony_ci if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { 46813498266Sopenharmony_ci infof(data, "WS: decode ending with %zd frame bytes remaining", 46913498266Sopenharmony_ci Curl_bufq_len(&ctx->buf)); 47013498266Sopenharmony_ci return CURLE_RECV_ERROR; 47113498266Sopenharmony_ci } 47213498266Sopenharmony_ci 47313498266Sopenharmony_ci return CURLE_OK; 47413498266Sopenharmony_ci} 47513498266Sopenharmony_ci 47613498266Sopenharmony_ci/* WebSocket payload decoding client writer. */ 47713498266Sopenharmony_cistatic const struct Curl_cwtype ws_cw_decode = { 47813498266Sopenharmony_ci "ws-decode", 47913498266Sopenharmony_ci NULL, 48013498266Sopenharmony_ci ws_cw_init, 48113498266Sopenharmony_ci ws_cw_write, 48213498266Sopenharmony_ci ws_cw_close, 48313498266Sopenharmony_ci sizeof(struct ws_cw_ctx) 48413498266Sopenharmony_ci}; 48513498266Sopenharmony_ci 48613498266Sopenharmony_ci 48713498266Sopenharmony_cistatic void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, 48813498266Sopenharmony_ci const char *msg) 48913498266Sopenharmony_ci{ 49013498266Sopenharmony_ci infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T 49113498266Sopenharmony_ci "/%" CURL_FORMAT_CURL_OFF_T "]", 49213498266Sopenharmony_ci msg, ws_frame_name_of_op(enc->firstbyte), 49313498266Sopenharmony_ci (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ? 49413498266Sopenharmony_ci " CONT" : "", 49513498266Sopenharmony_ci (enc->firstbyte & WSBIT_FIN)? "" : " NON-FIN", 49613498266Sopenharmony_ci enc->payload_len - enc->payload_remain, enc->payload_len); 49713498266Sopenharmony_ci} 49813498266Sopenharmony_ci 49913498266Sopenharmony_cistatic void ws_enc_reset(struct ws_encoder *enc) 50013498266Sopenharmony_ci{ 50113498266Sopenharmony_ci enc->payload_remain = 0; 50213498266Sopenharmony_ci enc->xori = 0; 50313498266Sopenharmony_ci enc->contfragment = FALSE; 50413498266Sopenharmony_ci} 50513498266Sopenharmony_ci 50613498266Sopenharmony_cistatic void ws_enc_init(struct ws_encoder *enc) 50713498266Sopenharmony_ci{ 50813498266Sopenharmony_ci ws_enc_reset(enc); 50913498266Sopenharmony_ci} 51013498266Sopenharmony_ci 51113498266Sopenharmony_ci/*** 51213498266Sopenharmony_ci RFC 6455 Section 5.2 51313498266Sopenharmony_ci 51413498266Sopenharmony_ci 0 1 2 3 51513498266Sopenharmony_ci 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 51613498266Sopenharmony_ci +-+-+-+-+-------+-+-------------+-------------------------------+ 51713498266Sopenharmony_ci |F|R|R|R| opcode|M| Payload len | Extended payload length | 51813498266Sopenharmony_ci |I|S|S|S| (4) |A| (7) | (16/64) | 51913498266Sopenharmony_ci |N|V|V|V| |S| | (if payload len==126/127) | 52013498266Sopenharmony_ci | |1|2|3| |K| | | 52113498266Sopenharmony_ci +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 52213498266Sopenharmony_ci | Extended payload length continued, if payload len == 127 | 52313498266Sopenharmony_ci + - - - - - - - - - - - - - - - +-------------------------------+ 52413498266Sopenharmony_ci | |Masking-key, if MASK set to 1 | 52513498266Sopenharmony_ci +-------------------------------+-------------------------------+ 52613498266Sopenharmony_ci | Masking-key (continued) | Payload Data | 52713498266Sopenharmony_ci +-------------------------------- - - - - - - - - - - - - - - - + 52813498266Sopenharmony_ci : Payload Data continued ... : 52913498266Sopenharmony_ci + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 53013498266Sopenharmony_ci | Payload Data continued ... | 53113498266Sopenharmony_ci +---------------------------------------------------------------+ 53213498266Sopenharmony_ci*/ 53313498266Sopenharmony_ci 53413498266Sopenharmony_cistatic ssize_t ws_enc_write_head(struct Curl_easy *data, 53513498266Sopenharmony_ci struct ws_encoder *enc, 53613498266Sopenharmony_ci unsigned int flags, 53713498266Sopenharmony_ci curl_off_t payload_len, 53813498266Sopenharmony_ci struct bufq *out, 53913498266Sopenharmony_ci CURLcode *err) 54013498266Sopenharmony_ci{ 54113498266Sopenharmony_ci unsigned char firstbyte = 0; 54213498266Sopenharmony_ci unsigned char opcode; 54313498266Sopenharmony_ci unsigned char head[14]; 54413498266Sopenharmony_ci size_t hlen; 54513498266Sopenharmony_ci ssize_t n; 54613498266Sopenharmony_ci 54713498266Sopenharmony_ci if(payload_len < 0) { 54813498266Sopenharmony_ci failf(data, "WS: starting new frame with negative payload length %" 54913498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T, payload_len); 55013498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 55113498266Sopenharmony_ci return -1; 55213498266Sopenharmony_ci } 55313498266Sopenharmony_ci 55413498266Sopenharmony_ci if(enc->payload_remain > 0) { 55513498266Sopenharmony_ci /* trying to write a new frame before the previous one is finished */ 55613498266Sopenharmony_ci failf(data, "WS: starting new frame with %zd bytes from last one" 55713498266Sopenharmony_ci "remaining to be sent", (ssize_t)enc->payload_remain); 55813498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 55913498266Sopenharmony_ci return -1; 56013498266Sopenharmony_ci } 56113498266Sopenharmony_ci 56213498266Sopenharmony_ci opcode = ws_frame_flags2op(flags); 56313498266Sopenharmony_ci if(!opcode) { 56413498266Sopenharmony_ci failf(data, "WS: provided flags not recognized '%x'", flags); 56513498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 56613498266Sopenharmony_ci return -1; 56713498266Sopenharmony_ci } 56813498266Sopenharmony_ci 56913498266Sopenharmony_ci if(!(flags & CURLWS_CONT)) { 57013498266Sopenharmony_ci if(!enc->contfragment) 57113498266Sopenharmony_ci /* not marked as continuing, this is the final fragment */ 57213498266Sopenharmony_ci firstbyte |= WSBIT_FIN | opcode; 57313498266Sopenharmony_ci else 57413498266Sopenharmony_ci /* marked as continuing, this is the final fragment; set CONT 57513498266Sopenharmony_ci opcode and FIN bit */ 57613498266Sopenharmony_ci firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT; 57713498266Sopenharmony_ci 57813498266Sopenharmony_ci enc->contfragment = FALSE; 57913498266Sopenharmony_ci } 58013498266Sopenharmony_ci else if(enc->contfragment) { 58113498266Sopenharmony_ci /* the previous fragment was not a final one and this isn't either, keep a 58213498266Sopenharmony_ci CONT opcode and no FIN bit */ 58313498266Sopenharmony_ci firstbyte |= WSBIT_OPCODE_CONT; 58413498266Sopenharmony_ci } 58513498266Sopenharmony_ci else { 58613498266Sopenharmony_ci firstbyte = opcode; 58713498266Sopenharmony_ci enc->contfragment = TRUE; 58813498266Sopenharmony_ci } 58913498266Sopenharmony_ci 59013498266Sopenharmony_ci head[0] = enc->firstbyte = firstbyte; 59113498266Sopenharmony_ci if(payload_len > 65535) { 59213498266Sopenharmony_ci head[1] = 127 | WSBIT_MASK; 59313498266Sopenharmony_ci head[2] = (unsigned char)((payload_len >> 56) & 0xff); 59413498266Sopenharmony_ci head[3] = (unsigned char)((payload_len >> 48) & 0xff); 59513498266Sopenharmony_ci head[4] = (unsigned char)((payload_len >> 40) & 0xff); 59613498266Sopenharmony_ci head[5] = (unsigned char)((payload_len >> 32) & 0xff); 59713498266Sopenharmony_ci head[6] = (unsigned char)((payload_len >> 24) & 0xff); 59813498266Sopenharmony_ci head[7] = (unsigned char)((payload_len >> 16) & 0xff); 59913498266Sopenharmony_ci head[8] = (unsigned char)((payload_len >> 8) & 0xff); 60013498266Sopenharmony_ci head[9] = (unsigned char)(payload_len & 0xff); 60113498266Sopenharmony_ci hlen = 10; 60213498266Sopenharmony_ci } 60313498266Sopenharmony_ci else if(payload_len >= 126) { 60413498266Sopenharmony_ci head[1] = 126 | WSBIT_MASK; 60513498266Sopenharmony_ci head[2] = (unsigned char)((payload_len >> 8) & 0xff); 60613498266Sopenharmony_ci head[3] = (unsigned char)(payload_len & 0xff); 60713498266Sopenharmony_ci hlen = 4; 60813498266Sopenharmony_ci } 60913498266Sopenharmony_ci else { 61013498266Sopenharmony_ci head[1] = (unsigned char)payload_len | WSBIT_MASK; 61113498266Sopenharmony_ci hlen = 2; 61213498266Sopenharmony_ci } 61313498266Sopenharmony_ci 61413498266Sopenharmony_ci enc->payload_remain = enc->payload_len = payload_len; 61513498266Sopenharmony_ci ws_enc_info(enc, data, "sending"); 61613498266Sopenharmony_ci 61713498266Sopenharmony_ci /* add 4 bytes mask */ 61813498266Sopenharmony_ci memcpy(&head[hlen], &enc->mask, 4); 61913498266Sopenharmony_ci hlen += 4; 62013498266Sopenharmony_ci /* reset for payload to come */ 62113498266Sopenharmony_ci enc->xori = 0; 62213498266Sopenharmony_ci 62313498266Sopenharmony_ci n = Curl_bufq_write(out, head, hlen, err); 62413498266Sopenharmony_ci if(n < 0) 62513498266Sopenharmony_ci return -1; 62613498266Sopenharmony_ci if((size_t)n != hlen) { 62713498266Sopenharmony_ci /* We use a bufq with SOFT_LIMIT, writing should always succeed */ 62813498266Sopenharmony_ci DEBUGASSERT(0); 62913498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 63013498266Sopenharmony_ci return -1; 63113498266Sopenharmony_ci } 63213498266Sopenharmony_ci return n; 63313498266Sopenharmony_ci} 63413498266Sopenharmony_ci 63513498266Sopenharmony_cistatic ssize_t ws_enc_write_payload(struct ws_encoder *enc, 63613498266Sopenharmony_ci struct Curl_easy *data, 63713498266Sopenharmony_ci const unsigned char *buf, size_t buflen, 63813498266Sopenharmony_ci struct bufq *out, CURLcode *err) 63913498266Sopenharmony_ci{ 64013498266Sopenharmony_ci ssize_t n; 64113498266Sopenharmony_ci size_t i, len; 64213498266Sopenharmony_ci 64313498266Sopenharmony_ci if(Curl_bufq_is_full(out)) { 64413498266Sopenharmony_ci *err = CURLE_AGAIN; 64513498266Sopenharmony_ci return -1; 64613498266Sopenharmony_ci } 64713498266Sopenharmony_ci 64813498266Sopenharmony_ci /* not the most performant way to do this */ 64913498266Sopenharmony_ci len = buflen; 65013498266Sopenharmony_ci if((curl_off_t)len > enc->payload_remain) 65113498266Sopenharmony_ci len = (size_t)enc->payload_remain; 65213498266Sopenharmony_ci 65313498266Sopenharmony_ci for(i = 0; i < len; ++i) { 65413498266Sopenharmony_ci unsigned char c = buf[i] ^ enc->mask[enc->xori]; 65513498266Sopenharmony_ci n = Curl_bufq_write(out, &c, 1, err); 65613498266Sopenharmony_ci if(n < 0) { 65713498266Sopenharmony_ci if((*err != CURLE_AGAIN) || !i) 65813498266Sopenharmony_ci return -1; 65913498266Sopenharmony_ci break; 66013498266Sopenharmony_ci } 66113498266Sopenharmony_ci enc->xori++; 66213498266Sopenharmony_ci enc->xori &= 3; 66313498266Sopenharmony_ci } 66413498266Sopenharmony_ci enc->payload_remain -= (curl_off_t)i; 66513498266Sopenharmony_ci ws_enc_info(enc, data, "buffered"); 66613498266Sopenharmony_ci return (ssize_t)i; 66713498266Sopenharmony_ci} 66813498266Sopenharmony_ci 66913498266Sopenharmony_ci 67013498266Sopenharmony_cistruct wsfield { 67113498266Sopenharmony_ci const char *name; 67213498266Sopenharmony_ci const char *val; 67313498266Sopenharmony_ci}; 67413498266Sopenharmony_ci 67513498266Sopenharmony_ciCURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) 67613498266Sopenharmony_ci{ 67713498266Sopenharmony_ci unsigned int i; 67813498266Sopenharmony_ci CURLcode result = CURLE_OK; 67913498266Sopenharmony_ci unsigned char rand[16]; 68013498266Sopenharmony_ci char *randstr; 68113498266Sopenharmony_ci size_t randlen; 68213498266Sopenharmony_ci char keyval[40]; 68313498266Sopenharmony_ci struct SingleRequest *k = &data->req; 68413498266Sopenharmony_ci struct wsfield heads[]= { 68513498266Sopenharmony_ci { 68613498266Sopenharmony_ci /* The request MUST contain an |Upgrade| header field whose value 68713498266Sopenharmony_ci MUST include the "websocket" keyword. */ 68813498266Sopenharmony_ci "Upgrade:", "websocket" 68913498266Sopenharmony_ci }, 69013498266Sopenharmony_ci { 69113498266Sopenharmony_ci /* The request MUST contain a |Connection| header field whose value 69213498266Sopenharmony_ci MUST include the "Upgrade" token. */ 69313498266Sopenharmony_ci "Connection:", "Upgrade", 69413498266Sopenharmony_ci }, 69513498266Sopenharmony_ci { 69613498266Sopenharmony_ci /* The request MUST include a header field with the name 69713498266Sopenharmony_ci |Sec-WebSocket-Version|. The value of this header field MUST be 69813498266Sopenharmony_ci 13. */ 69913498266Sopenharmony_ci "Sec-WebSocket-Version:", "13", 70013498266Sopenharmony_ci }, 70113498266Sopenharmony_ci { 70213498266Sopenharmony_ci /* The request MUST include a header field with the name 70313498266Sopenharmony_ci |Sec-WebSocket-Key|. The value of this header field MUST be a nonce 70413498266Sopenharmony_ci consisting of a randomly selected 16-byte value that has been 70513498266Sopenharmony_ci base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be 70613498266Sopenharmony_ci selected randomly for each connection. */ 70713498266Sopenharmony_ci "Sec-WebSocket-Key:", NULL, 70813498266Sopenharmony_ci } 70913498266Sopenharmony_ci }; 71013498266Sopenharmony_ci heads[3].val = &keyval[0]; 71113498266Sopenharmony_ci 71213498266Sopenharmony_ci /* 16 bytes random */ 71313498266Sopenharmony_ci result = Curl_rand(data, (unsigned char *)rand, sizeof(rand)); 71413498266Sopenharmony_ci if(result) 71513498266Sopenharmony_ci return result; 71613498266Sopenharmony_ci result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen); 71713498266Sopenharmony_ci if(result) 71813498266Sopenharmony_ci return result; 71913498266Sopenharmony_ci DEBUGASSERT(randlen < sizeof(keyval)); 72013498266Sopenharmony_ci if(randlen >= sizeof(keyval)) 72113498266Sopenharmony_ci return CURLE_FAILED_INIT; 72213498266Sopenharmony_ci strcpy(keyval, randstr); 72313498266Sopenharmony_ci free(randstr); 72413498266Sopenharmony_ci for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) { 72513498266Sopenharmony_ci if(!Curl_checkheaders(data, STRCONST(heads[i].name))) { 72613498266Sopenharmony_ci#ifdef USE_HYPER 72713498266Sopenharmony_ci char field[128]; 72813498266Sopenharmony_ci msnprintf(field, sizeof(field), "%s %s", heads[i].name, 72913498266Sopenharmony_ci heads[i].val); 73013498266Sopenharmony_ci result = Curl_hyper_header(data, req, field); 73113498266Sopenharmony_ci#else 73213498266Sopenharmony_ci (void)data; 73313498266Sopenharmony_ci result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name, 73413498266Sopenharmony_ci heads[i].val); 73513498266Sopenharmony_ci#endif 73613498266Sopenharmony_ci } 73713498266Sopenharmony_ci } 73813498266Sopenharmony_ci k->upgr101 = UPGR101_WS; 73913498266Sopenharmony_ci return result; 74013498266Sopenharmony_ci} 74113498266Sopenharmony_ci 74213498266Sopenharmony_ci/* 74313498266Sopenharmony_ci * 'nread' is number of bytes of websocket data already in the buffer at 74413498266Sopenharmony_ci * 'mem'. 74513498266Sopenharmony_ci */ 74613498266Sopenharmony_ciCURLcode Curl_ws_accept(struct Curl_easy *data, 74713498266Sopenharmony_ci const char *mem, size_t nread) 74813498266Sopenharmony_ci{ 74913498266Sopenharmony_ci struct SingleRequest *k = &data->req; 75013498266Sopenharmony_ci struct websocket *ws; 75113498266Sopenharmony_ci struct Curl_cwriter *ws_dec_writer; 75213498266Sopenharmony_ci CURLcode result; 75313498266Sopenharmony_ci 75413498266Sopenharmony_ci DEBUGASSERT(data->conn); 75513498266Sopenharmony_ci ws = data->conn->proto.ws; 75613498266Sopenharmony_ci if(!ws) { 75713498266Sopenharmony_ci ws = calloc(1, sizeof(*ws)); 75813498266Sopenharmony_ci if(!ws) 75913498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 76013498266Sopenharmony_ci data->conn->proto.ws = ws; 76113498266Sopenharmony_ci Curl_bufq_init2(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT, 76213498266Sopenharmony_ci BUFQ_OPT_SOFT_LIMIT); 76313498266Sopenharmony_ci Curl_bufq_init2(&ws->sendbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT, 76413498266Sopenharmony_ci BUFQ_OPT_SOFT_LIMIT); 76513498266Sopenharmony_ci ws_dec_init(&ws->dec); 76613498266Sopenharmony_ci ws_enc_init(&ws->enc); 76713498266Sopenharmony_ci } 76813498266Sopenharmony_ci else { 76913498266Sopenharmony_ci Curl_bufq_reset(&ws->recvbuf); 77013498266Sopenharmony_ci ws_dec_reset(&ws->dec); 77113498266Sopenharmony_ci ws_enc_reset(&ws->enc); 77213498266Sopenharmony_ci } 77313498266Sopenharmony_ci /* Verify the Sec-WebSocket-Accept response. 77413498266Sopenharmony_ci 77513498266Sopenharmony_ci The sent value is the base64 encoded version of a SHA-1 hash done on the 77613498266Sopenharmony_ci |Sec-WebSocket-Key| header field concatenated with 77713498266Sopenharmony_ci the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11". 77813498266Sopenharmony_ci */ 77913498266Sopenharmony_ci 78013498266Sopenharmony_ci /* If the response includes a |Sec-WebSocket-Extensions| header field and 78113498266Sopenharmony_ci this header field indicates the use of an extension that was not present 78213498266Sopenharmony_ci in the client's handshake (the server has indicated an extension not 78313498266Sopenharmony_ci requested by the client), the client MUST Fail the WebSocket Connection. 78413498266Sopenharmony_ci */ 78513498266Sopenharmony_ci 78613498266Sopenharmony_ci /* If the response includes a |Sec-WebSocket-Protocol| header field 78713498266Sopenharmony_ci and this header field indicates the use of a subprotocol that was 78813498266Sopenharmony_ci not present in the client's handshake (the server has indicated a 78913498266Sopenharmony_ci subprotocol not requested by the client), the client MUST Fail 79013498266Sopenharmony_ci the WebSocket Connection. */ 79113498266Sopenharmony_ci 79213498266Sopenharmony_ci /* 4 bytes random */ 79313498266Sopenharmony_ci 79413498266Sopenharmony_ci result = Curl_rand(data, (unsigned char *)&ws->enc.mask, 79513498266Sopenharmony_ci sizeof(ws->enc.mask)); 79613498266Sopenharmony_ci if(result) 79713498266Sopenharmony_ci return result; 79813498266Sopenharmony_ci infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x", 79913498266Sopenharmony_ci ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]); 80013498266Sopenharmony_ci 80113498266Sopenharmony_ci /* Install our client writer that decodes WS frames payload */ 80213498266Sopenharmony_ci result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode, 80313498266Sopenharmony_ci CURL_CW_CONTENT_DECODE); 80413498266Sopenharmony_ci if(result) 80513498266Sopenharmony_ci return result; 80613498266Sopenharmony_ci 80713498266Sopenharmony_ci result = Curl_cwriter_add(data, ws_dec_writer); 80813498266Sopenharmony_ci if(result) { 80913498266Sopenharmony_ci Curl_cwriter_free(data, ws_dec_writer); 81013498266Sopenharmony_ci return result; 81113498266Sopenharmony_ci } 81213498266Sopenharmony_ci 81313498266Sopenharmony_ci if(data->set.connect_only) { 81413498266Sopenharmony_ci ssize_t nwritten; 81513498266Sopenharmony_ci /* In CONNECT_ONLY setup, the payloads from `mem` need to be received 81613498266Sopenharmony_ci * when using `curl_ws_recv` later on after this transfer is already 81713498266Sopenharmony_ci * marked as DONE. */ 81813498266Sopenharmony_ci nwritten = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem, 81913498266Sopenharmony_ci nread, &result); 82013498266Sopenharmony_ci if(nwritten < 0) 82113498266Sopenharmony_ci return result; 82213498266Sopenharmony_ci infof(data, "%zu bytes websocket payload", nread); 82313498266Sopenharmony_ci } 82413498266Sopenharmony_ci else { /* !connect_only */ 82513498266Sopenharmony_ci /* And pass any additional data to the writers */ 82613498266Sopenharmony_ci if(nread) { 82713498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)mem, nread); 82813498266Sopenharmony_ci } 82913498266Sopenharmony_ci } 83013498266Sopenharmony_ci k->upgr101 = UPGR101_RECEIVED; 83113498266Sopenharmony_ci 83213498266Sopenharmony_ci return result; 83313498266Sopenharmony_ci} 83413498266Sopenharmony_ci 83513498266Sopenharmony_cistruct ws_collect { 83613498266Sopenharmony_ci struct Curl_easy *data; 83713498266Sopenharmony_ci void *buffer; 83813498266Sopenharmony_ci size_t buflen; 83913498266Sopenharmony_ci size_t bufidx; 84013498266Sopenharmony_ci int frame_age; 84113498266Sopenharmony_ci int frame_flags; 84213498266Sopenharmony_ci curl_off_t payload_offset; 84313498266Sopenharmony_ci curl_off_t payload_len; 84413498266Sopenharmony_ci bool written; 84513498266Sopenharmony_ci}; 84613498266Sopenharmony_ci 84713498266Sopenharmony_cistatic ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, 84813498266Sopenharmony_ci int frame_age, int frame_flags, 84913498266Sopenharmony_ci curl_off_t payload_offset, 85013498266Sopenharmony_ci curl_off_t payload_len, 85113498266Sopenharmony_ci void *userp, 85213498266Sopenharmony_ci CURLcode *err) 85313498266Sopenharmony_ci{ 85413498266Sopenharmony_ci struct ws_collect *ctx = userp; 85513498266Sopenharmony_ci size_t nwritten; 85613498266Sopenharmony_ci curl_off_t remain = (payload_len - (payload_offset + buflen)); 85713498266Sopenharmony_ci 85813498266Sopenharmony_ci if(!ctx->bufidx) { 85913498266Sopenharmony_ci /* first write */ 86013498266Sopenharmony_ci ctx->frame_age = frame_age; 86113498266Sopenharmony_ci ctx->frame_flags = frame_flags; 86213498266Sopenharmony_ci ctx->payload_offset = payload_offset; 86313498266Sopenharmony_ci ctx->payload_len = payload_len; 86413498266Sopenharmony_ci } 86513498266Sopenharmony_ci 86613498266Sopenharmony_ci if((frame_flags & CURLWS_PING) && !remain) { 86713498266Sopenharmony_ci /* auto-respond to PINGs, only works for single-frame payloads atm */ 86813498266Sopenharmony_ci size_t bytes; 86913498266Sopenharmony_ci infof(ctx->data, "WS: auto-respond to PING with a PONG"); 87013498266Sopenharmony_ci /* send back the exact same content as a PONG */ 87113498266Sopenharmony_ci *err = curl_ws_send(ctx->data, buf, buflen, &bytes, 0, CURLWS_PONG); 87213498266Sopenharmony_ci if(*err) 87313498266Sopenharmony_ci return -1; 87413498266Sopenharmony_ci nwritten = bytes; 87513498266Sopenharmony_ci } 87613498266Sopenharmony_ci else { 87713498266Sopenharmony_ci ctx->written = TRUE; 87813498266Sopenharmony_ci DEBUGASSERT(ctx->buflen >= ctx->bufidx); 87913498266Sopenharmony_ci nwritten = CURLMIN(buflen, ctx->buflen - ctx->bufidx); 88013498266Sopenharmony_ci if(!nwritten) { 88113498266Sopenharmony_ci if(!buflen) { /* 0 length write, we accept that */ 88213498266Sopenharmony_ci *err = CURLE_OK; 88313498266Sopenharmony_ci return 0; 88413498266Sopenharmony_ci } 88513498266Sopenharmony_ci *err = CURLE_AGAIN; /* no more space */ 88613498266Sopenharmony_ci return -1; 88713498266Sopenharmony_ci } 88813498266Sopenharmony_ci *err = CURLE_OK; 88913498266Sopenharmony_ci memcpy(ctx->buffer, buf, nwritten); 89013498266Sopenharmony_ci ctx->bufidx += nwritten; 89113498266Sopenharmony_ci } 89213498266Sopenharmony_ci return nwritten; 89313498266Sopenharmony_ci} 89413498266Sopenharmony_ci 89513498266Sopenharmony_cistatic ssize_t nw_in_recv(void *reader_ctx, 89613498266Sopenharmony_ci unsigned char *buf, size_t buflen, 89713498266Sopenharmony_ci CURLcode *err) 89813498266Sopenharmony_ci{ 89913498266Sopenharmony_ci struct Curl_easy *data = reader_ctx; 90013498266Sopenharmony_ci size_t nread; 90113498266Sopenharmony_ci 90213498266Sopenharmony_ci *err = curl_easy_recv(data, buf, buflen, &nread); 90313498266Sopenharmony_ci if(*err) 90413498266Sopenharmony_ci return -1; 90513498266Sopenharmony_ci return (ssize_t)nread; 90613498266Sopenharmony_ci} 90713498266Sopenharmony_ci 90813498266Sopenharmony_ciCURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, 90913498266Sopenharmony_ci size_t buflen, size_t *nread, 91013498266Sopenharmony_ci const struct curl_ws_frame **metap) 91113498266Sopenharmony_ci{ 91213498266Sopenharmony_ci struct connectdata *conn = data->conn; 91313498266Sopenharmony_ci struct websocket *ws; 91413498266Sopenharmony_ci bool done = FALSE; /* not filled passed buffer yet */ 91513498266Sopenharmony_ci struct ws_collect ctx; 91613498266Sopenharmony_ci CURLcode result; 91713498266Sopenharmony_ci 91813498266Sopenharmony_ci if(!conn) { 91913498266Sopenharmony_ci /* Unhappy hack with lifetimes of transfers and connection */ 92013498266Sopenharmony_ci if(!data->set.connect_only) { 92113498266Sopenharmony_ci failf(data, "CONNECT_ONLY is required"); 92213498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 92313498266Sopenharmony_ci } 92413498266Sopenharmony_ci 92513498266Sopenharmony_ci Curl_getconnectinfo(data, &conn); 92613498266Sopenharmony_ci if(!conn) { 92713498266Sopenharmony_ci failf(data, "connection not found"); 92813498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 92913498266Sopenharmony_ci } 93013498266Sopenharmony_ci } 93113498266Sopenharmony_ci ws = conn->proto.ws; 93213498266Sopenharmony_ci if(!ws) { 93313498266Sopenharmony_ci failf(data, "connection is not setup for websocket"); 93413498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 93513498266Sopenharmony_ci } 93613498266Sopenharmony_ci 93713498266Sopenharmony_ci *nread = 0; 93813498266Sopenharmony_ci *metap = NULL; 93913498266Sopenharmony_ci /* get a download buffer */ 94013498266Sopenharmony_ci result = Curl_preconnect(data); 94113498266Sopenharmony_ci if(result) 94213498266Sopenharmony_ci return result; 94313498266Sopenharmony_ci 94413498266Sopenharmony_ci memset(&ctx, 0, sizeof(ctx)); 94513498266Sopenharmony_ci ctx.data = data; 94613498266Sopenharmony_ci ctx.buffer = buffer; 94713498266Sopenharmony_ci ctx.buflen = buflen; 94813498266Sopenharmony_ci 94913498266Sopenharmony_ci while(!done) { 95013498266Sopenharmony_ci /* receive more when our buffer is empty */ 95113498266Sopenharmony_ci if(Curl_bufq_is_empty(&ws->recvbuf)) { 95213498266Sopenharmony_ci ssize_t n = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &result); 95313498266Sopenharmony_ci if(n < 0) { 95413498266Sopenharmony_ci return result; 95513498266Sopenharmony_ci } 95613498266Sopenharmony_ci else if(n == 0) { 95713498266Sopenharmony_ci /* connection closed */ 95813498266Sopenharmony_ci infof(data, "connection expectedly closed?"); 95913498266Sopenharmony_ci return CURLE_GOT_NOTHING; 96013498266Sopenharmony_ci } 96113498266Sopenharmony_ci DEBUGF(infof(data, "curl_ws_recv, added %zu bytes from network", 96213498266Sopenharmony_ci Curl_bufq_len(&ws->recvbuf))); 96313498266Sopenharmony_ci } 96413498266Sopenharmony_ci 96513498266Sopenharmony_ci result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, 96613498266Sopenharmony_ci ws_client_collect, &ctx); 96713498266Sopenharmony_ci if(result == CURLE_AGAIN) { 96813498266Sopenharmony_ci if(!ctx.written) { 96913498266Sopenharmony_ci ws_dec_info(&ws->dec, data, "need more input"); 97013498266Sopenharmony_ci continue; /* nothing written, try more input */ 97113498266Sopenharmony_ci } 97213498266Sopenharmony_ci done = TRUE; 97313498266Sopenharmony_ci break; 97413498266Sopenharmony_ci } 97513498266Sopenharmony_ci else if(result) { 97613498266Sopenharmony_ci return result; 97713498266Sopenharmony_ci } 97813498266Sopenharmony_ci else if(ctx.written) { 97913498266Sopenharmony_ci /* The decoded frame is passed back to our caller. 98013498266Sopenharmony_ci * There are frames like PING were we auto-respond to and 98113498266Sopenharmony_ci * that we do not return. For these `ctx.written` is not set. */ 98213498266Sopenharmony_ci done = TRUE; 98313498266Sopenharmony_ci break; 98413498266Sopenharmony_ci } 98513498266Sopenharmony_ci } 98613498266Sopenharmony_ci 98713498266Sopenharmony_ci /* update frame information to be passed back */ 98813498266Sopenharmony_ci update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, 98913498266Sopenharmony_ci ctx.payload_len, ctx.bufidx); 99013498266Sopenharmony_ci *metap = &ws->frame; 99113498266Sopenharmony_ci *nread = ws->frame.len; 99213498266Sopenharmony_ci /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" 99313498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", 99413498266Sopenharmony_ci buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ 99513498266Sopenharmony_ci return CURLE_OK; 99613498266Sopenharmony_ci} 99713498266Sopenharmony_ci 99813498266Sopenharmony_cistatic CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, 99913498266Sopenharmony_ci bool complete) 100013498266Sopenharmony_ci{ 100113498266Sopenharmony_ci if(!Curl_bufq_is_empty(&ws->sendbuf)) { 100213498266Sopenharmony_ci CURLcode result; 100313498266Sopenharmony_ci const unsigned char *out; 100413498266Sopenharmony_ci size_t outlen; 100513498266Sopenharmony_ci ssize_t n; 100613498266Sopenharmony_ci 100713498266Sopenharmony_ci while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) { 100813498266Sopenharmony_ci if(data->set.connect_only) 100913498266Sopenharmony_ci result = Curl_senddata(data, out, outlen, &n); 101013498266Sopenharmony_ci else 101113498266Sopenharmony_ci result = Curl_write(data, data->conn->writesockfd, out, outlen, &n); 101213498266Sopenharmony_ci if(result) { 101313498266Sopenharmony_ci if(result == CURLE_AGAIN) { 101413498266Sopenharmony_ci if(!complete) { 101513498266Sopenharmony_ci infof(data, "WS: flush EAGAIN, %zu bytes remain in buffer", 101613498266Sopenharmony_ci Curl_bufq_len(&ws->sendbuf)); 101713498266Sopenharmony_ci return result; 101813498266Sopenharmony_ci } 101913498266Sopenharmony_ci /* TODO: the current design does not allow for buffered writes. 102013498266Sopenharmony_ci * We need to flush the buffer now. There is no ws_flush() later */ 102113498266Sopenharmony_ci n = 0; 102213498266Sopenharmony_ci continue; 102313498266Sopenharmony_ci } 102413498266Sopenharmony_ci else if(result) { 102513498266Sopenharmony_ci failf(data, "WS: flush, write error %d", result); 102613498266Sopenharmony_ci return result; 102713498266Sopenharmony_ci } 102813498266Sopenharmony_ci } 102913498266Sopenharmony_ci else { 103013498266Sopenharmony_ci infof(data, "WS: flushed %zu bytes", (size_t)n); 103113498266Sopenharmony_ci Curl_bufq_skip(&ws->sendbuf, (size_t)n); 103213498266Sopenharmony_ci } 103313498266Sopenharmony_ci } 103413498266Sopenharmony_ci } 103513498266Sopenharmony_ci return CURLE_OK; 103613498266Sopenharmony_ci} 103713498266Sopenharmony_ci 103813498266Sopenharmony_ciCURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, 103913498266Sopenharmony_ci size_t buflen, size_t *sent, 104013498266Sopenharmony_ci curl_off_t fragsize, 104113498266Sopenharmony_ci unsigned int flags) 104213498266Sopenharmony_ci{ 104313498266Sopenharmony_ci struct websocket *ws; 104413498266Sopenharmony_ci ssize_t nwritten, n; 104513498266Sopenharmony_ci size_t space; 104613498266Sopenharmony_ci CURLcode result; 104713498266Sopenharmony_ci 104813498266Sopenharmony_ci *sent = 0; 104913498266Sopenharmony_ci if(!data->conn && data->set.connect_only) { 105013498266Sopenharmony_ci result = Curl_connect_only_attach(data); 105113498266Sopenharmony_ci if(result) 105213498266Sopenharmony_ci return result; 105313498266Sopenharmony_ci } 105413498266Sopenharmony_ci if(!data->conn) { 105513498266Sopenharmony_ci failf(data, "No associated connection"); 105613498266Sopenharmony_ci return CURLE_SEND_ERROR; 105713498266Sopenharmony_ci } 105813498266Sopenharmony_ci if(!data->conn->proto.ws) { 105913498266Sopenharmony_ci failf(data, "Not a websocket transfer"); 106013498266Sopenharmony_ci return CURLE_SEND_ERROR; 106113498266Sopenharmony_ci } 106213498266Sopenharmony_ci ws = data->conn->proto.ws; 106313498266Sopenharmony_ci 106413498266Sopenharmony_ci if(data->set.ws_raw_mode) { 106513498266Sopenharmony_ci if(fragsize || flags) { 106613498266Sopenharmony_ci DEBUGF(infof(data, "ws_send: " 106713498266Sopenharmony_ci "fragsize and flags cannot be non-zero in raw mode")); 106813498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 106913498266Sopenharmony_ci } 107013498266Sopenharmony_ci if(!buflen) 107113498266Sopenharmony_ci /* nothing to do */ 107213498266Sopenharmony_ci return CURLE_OK; 107313498266Sopenharmony_ci /* raw mode sends exactly what was requested, and this is from within 107413498266Sopenharmony_ci the write callback */ 107513498266Sopenharmony_ci if(Curl_is_in_callback(data)) { 107613498266Sopenharmony_ci result = Curl_write(data, data->conn->writesockfd, buffer, buflen, 107713498266Sopenharmony_ci &nwritten); 107813498266Sopenharmony_ci } 107913498266Sopenharmony_ci else 108013498266Sopenharmony_ci result = Curl_senddata(data, buffer, buflen, &nwritten); 108113498266Sopenharmony_ci 108213498266Sopenharmony_ci infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", 108313498266Sopenharmony_ci buflen, nwritten); 108413498266Sopenharmony_ci *sent = (nwritten >= 0)? (size_t)nwritten : 0; 108513498266Sopenharmony_ci return result; 108613498266Sopenharmony_ci } 108713498266Sopenharmony_ci 108813498266Sopenharmony_ci /* Not RAW mode, buf we do the frame encoding */ 108913498266Sopenharmony_ci result = ws_flush(data, ws, FALSE); 109013498266Sopenharmony_ci if(result) 109113498266Sopenharmony_ci return result; 109213498266Sopenharmony_ci 109313498266Sopenharmony_ci /* TODO: the current design does not allow partial writes, afaict. 109413498266Sopenharmony_ci * It is not clear who the application is supposed to react. */ 109513498266Sopenharmony_ci space = Curl_bufq_space(&ws->sendbuf); 109613498266Sopenharmony_ci DEBUGF(infof(data, "curl_ws_send(len=%zu), sendbuf len=%zu space %zu", 109713498266Sopenharmony_ci buflen, Curl_bufq_len(&ws->sendbuf), space)); 109813498266Sopenharmony_ci if(space < 14) 109913498266Sopenharmony_ci return CURLE_AGAIN; 110013498266Sopenharmony_ci 110113498266Sopenharmony_ci if(flags & CURLWS_OFFSET) { 110213498266Sopenharmony_ci if(fragsize) { 110313498266Sopenharmony_ci /* a frame series 'fragsize' bytes big, this is the first */ 110413498266Sopenharmony_ci n = ws_enc_write_head(data, &ws->enc, flags, fragsize, 110513498266Sopenharmony_ci &ws->sendbuf, &result); 110613498266Sopenharmony_ci if(n < 0) 110713498266Sopenharmony_ci return result; 110813498266Sopenharmony_ci } 110913498266Sopenharmony_ci else { 111013498266Sopenharmony_ci if((curl_off_t)buflen > ws->enc.payload_remain) { 111113498266Sopenharmony_ci infof(data, "WS: unaligned frame size (sending %zu instead of %" 111213498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ")", 111313498266Sopenharmony_ci buflen, ws->enc.payload_remain); 111413498266Sopenharmony_ci } 111513498266Sopenharmony_ci } 111613498266Sopenharmony_ci } 111713498266Sopenharmony_ci else if(!ws->enc.payload_remain) { 111813498266Sopenharmony_ci n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen, 111913498266Sopenharmony_ci &ws->sendbuf, &result); 112013498266Sopenharmony_ci if(n < 0) 112113498266Sopenharmony_ci return result; 112213498266Sopenharmony_ci } 112313498266Sopenharmony_ci 112413498266Sopenharmony_ci n = ws_enc_write_payload(&ws->enc, data, 112513498266Sopenharmony_ci buffer, buflen, &ws->sendbuf, &result); 112613498266Sopenharmony_ci if(n < 0) 112713498266Sopenharmony_ci return result; 112813498266Sopenharmony_ci 112913498266Sopenharmony_ci *sent = (size_t)n; 113013498266Sopenharmony_ci return ws_flush(data, ws, TRUE); 113113498266Sopenharmony_ci} 113213498266Sopenharmony_ci 113313498266Sopenharmony_cistatic void ws_free(struct connectdata *conn) 113413498266Sopenharmony_ci{ 113513498266Sopenharmony_ci if(conn && conn->proto.ws) { 113613498266Sopenharmony_ci Curl_bufq_free(&conn->proto.ws->recvbuf); 113713498266Sopenharmony_ci Curl_bufq_free(&conn->proto.ws->sendbuf); 113813498266Sopenharmony_ci Curl_safefree(conn->proto.ws); 113913498266Sopenharmony_ci } 114013498266Sopenharmony_ci} 114113498266Sopenharmony_ci 114213498266Sopenharmony_cistatic CURLcode ws_setup_conn(struct Curl_easy *data, 114313498266Sopenharmony_ci struct connectdata *conn) 114413498266Sopenharmony_ci{ 114513498266Sopenharmony_ci /* websockets is 1.1 only (for now) */ 114613498266Sopenharmony_ci data->state.httpwant = CURL_HTTP_VERSION_1_1; 114713498266Sopenharmony_ci return Curl_http_setup_conn(data, conn); 114813498266Sopenharmony_ci} 114913498266Sopenharmony_ci 115013498266Sopenharmony_ci 115113498266Sopenharmony_civoid Curl_ws_done(struct Curl_easy *data) 115213498266Sopenharmony_ci{ 115313498266Sopenharmony_ci (void)data; 115413498266Sopenharmony_ci} 115513498266Sopenharmony_ci 115613498266Sopenharmony_cistatic CURLcode ws_disconnect(struct Curl_easy *data, 115713498266Sopenharmony_ci struct connectdata *conn, 115813498266Sopenharmony_ci bool dead_connection) 115913498266Sopenharmony_ci{ 116013498266Sopenharmony_ci (void)data; 116113498266Sopenharmony_ci (void)dead_connection; 116213498266Sopenharmony_ci ws_free(conn); 116313498266Sopenharmony_ci return CURLE_OK; 116413498266Sopenharmony_ci} 116513498266Sopenharmony_ci 116613498266Sopenharmony_ciCURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) 116713498266Sopenharmony_ci{ 116813498266Sopenharmony_ci /* we only return something for websocket, called from within the callback 116913498266Sopenharmony_ci when not using raw mode */ 117013498266Sopenharmony_ci if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn && 117113498266Sopenharmony_ci data->conn->proto.ws && !data->set.ws_raw_mode) 117213498266Sopenharmony_ci return &data->conn->proto.ws->frame; 117313498266Sopenharmony_ci return NULL; 117413498266Sopenharmony_ci} 117513498266Sopenharmony_ci 117613498266Sopenharmony_ciconst struct Curl_handler Curl_handler_ws = { 117713498266Sopenharmony_ci "WS", /* scheme */ 117813498266Sopenharmony_ci ws_setup_conn, /* setup_connection */ 117913498266Sopenharmony_ci Curl_http, /* do_it */ 118013498266Sopenharmony_ci Curl_http_done, /* done */ 118113498266Sopenharmony_ci ZERO_NULL, /* do_more */ 118213498266Sopenharmony_ci Curl_http_connect, /* connect_it */ 118313498266Sopenharmony_ci ZERO_NULL, /* connecting */ 118413498266Sopenharmony_ci ZERO_NULL, /* doing */ 118513498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 118613498266Sopenharmony_ci Curl_http_getsock_do, /* doing_getsock */ 118713498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 118813498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 118913498266Sopenharmony_ci ws_disconnect, /* disconnect */ 119013498266Sopenharmony_ci Curl_http_write_resp, /* write_resp */ 119113498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 119213498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 119313498266Sopenharmony_ci PORT_HTTP, /* defport */ 119413498266Sopenharmony_ci CURLPROTO_WS, /* protocol */ 119513498266Sopenharmony_ci CURLPROTO_HTTP, /* family */ 119613498266Sopenharmony_ci PROTOPT_CREDSPERREQUEST | /* flags */ 119713498266Sopenharmony_ci PROTOPT_USERPWDCTRL 119813498266Sopenharmony_ci}; 119913498266Sopenharmony_ci 120013498266Sopenharmony_ci#ifdef USE_SSL 120113498266Sopenharmony_ciconst struct Curl_handler Curl_handler_wss = { 120213498266Sopenharmony_ci "WSS", /* scheme */ 120313498266Sopenharmony_ci ws_setup_conn, /* setup_connection */ 120413498266Sopenharmony_ci Curl_http, /* do_it */ 120513498266Sopenharmony_ci Curl_http_done, /* done */ 120613498266Sopenharmony_ci ZERO_NULL, /* do_more */ 120713498266Sopenharmony_ci Curl_http_connect, /* connect_it */ 120813498266Sopenharmony_ci NULL, /* connecting */ 120913498266Sopenharmony_ci ZERO_NULL, /* doing */ 121013498266Sopenharmony_ci NULL, /* proto_getsock */ 121113498266Sopenharmony_ci Curl_http_getsock_do, /* doing_getsock */ 121213498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 121313498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 121413498266Sopenharmony_ci ws_disconnect, /* disconnect */ 121513498266Sopenharmony_ci Curl_http_write_resp, /* write_resp */ 121613498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 121713498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 121813498266Sopenharmony_ci PORT_HTTPS, /* defport */ 121913498266Sopenharmony_ci CURLPROTO_WSS, /* protocol */ 122013498266Sopenharmony_ci CURLPROTO_HTTP, /* family */ 122113498266Sopenharmony_ci PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ 122213498266Sopenharmony_ci PROTOPT_USERPWDCTRL 122313498266Sopenharmony_ci}; 122413498266Sopenharmony_ci#endif 122513498266Sopenharmony_ci 122613498266Sopenharmony_ci 122713498266Sopenharmony_ci#else 122813498266Sopenharmony_ci 122913498266Sopenharmony_ciCURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, 123013498266Sopenharmony_ci size_t *nread, 123113498266Sopenharmony_ci const struct curl_ws_frame **metap) 123213498266Sopenharmony_ci{ 123313498266Sopenharmony_ci (void)curl; 123413498266Sopenharmony_ci (void)buffer; 123513498266Sopenharmony_ci (void)buflen; 123613498266Sopenharmony_ci (void)nread; 123713498266Sopenharmony_ci (void)metap; 123813498266Sopenharmony_ci return CURLE_NOT_BUILT_IN; 123913498266Sopenharmony_ci} 124013498266Sopenharmony_ci 124113498266Sopenharmony_ciCURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, 124213498266Sopenharmony_ci size_t buflen, size_t *sent, 124313498266Sopenharmony_ci curl_off_t fragsize, 124413498266Sopenharmony_ci unsigned int flags) 124513498266Sopenharmony_ci{ 124613498266Sopenharmony_ci (void)curl; 124713498266Sopenharmony_ci (void)buffer; 124813498266Sopenharmony_ci (void)buflen; 124913498266Sopenharmony_ci (void)sent; 125013498266Sopenharmony_ci (void)fragsize; 125113498266Sopenharmony_ci (void)flags; 125213498266Sopenharmony_ci return CURLE_NOT_BUILT_IN; 125313498266Sopenharmony_ci} 125413498266Sopenharmony_ci 125513498266Sopenharmony_ciCURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) 125613498266Sopenharmony_ci{ 125713498266Sopenharmony_ci (void)data; 125813498266Sopenharmony_ci return NULL; 125913498266Sopenharmony_ci} 126013498266Sopenharmony_ci#endif /* USE_WEBSOCKETS */ 1261