diff options
author | Daniel Stenberg <daniel@haxx.se> | 2023-02-08 14:24:49 +0100 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2023-02-10 08:28:58 +0100 |
commit | 2e2e3d16c5c77596a789f6e6a1b30be7fc17360c (patch) | |
tree | ee4d94b897e1b3381c61df7fad4c0d6a334dac98 /lib | |
parent | 2aed8e179f0675f5b17d75f3904ea7d905f4336a (diff) | |
download | curl-2e2e3d16c5c77596a789f6e6a1b30be7fc17360c.tar.gz |
ws: fix recv of larger frames
+ remove 'oleft' from the struct
+ deal with "overflow data" in a separate dynbuf
Reported-by: Mike Duglas
Fixes #10438
Closes #10447
Diffstat (limited to 'lib')
-rw-r--r-- | lib/c-hyper.c | 2 | ||||
-rw-r--r-- | lib/easy.c | 2 | ||||
-rw-r--r-- | lib/http.c | 2 | ||||
-rw-r--r-- | lib/urldata.h | 3 | ||||
-rw-r--r-- | lib/ws.c | 53 | ||||
-rw-r--r-- | lib/ws.h | 13 |
6 files changed, 52 insertions, 23 deletions
diff --git a/lib/c-hyper.c b/lib/c-hyper.c index 74856d9a5..9c7632d35 100644 --- a/lib/c-hyper.c +++ b/lib/c-hyper.c @@ -485,7 +485,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, if(k->upgr101 == UPGR101_WS) { if(http_status == 101) { /* verify the response */ - result = Curl_ws_accept(data); + result = Curl_ws_accept(data, NULL, 0); if(result) return result; } diff --git a/lib/easy.c b/lib/easy.c index 2cd1ada4e..db03026c9 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -1228,7 +1228,7 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, return result; *n = (size_t)n1; - + infof(data, "reached %s:%d", __FILE__, __LINE__); return CURLE_OK; } diff --git a/lib/http.c b/lib/http.c index 3eba1a375..cb585e72f 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3909,7 +3909,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, #ifdef USE_WEBSOCKETS else if(k->upgr101 == UPGR101_WS) { /* verify the response */ - result = Curl_ws_accept(data); + result = Curl_ws_accept(data, k->str, *nread); if(result) return result; k->header = FALSE; /* no more header to parse! */ diff --git a/lib/urldata.h b/lib/urldata.h index 19c5ac7e3..0e2c04a04 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1015,6 +1015,9 @@ struct connectdata { #ifndef CURL_DISABLE_MQTT struct mqtt_conn mqtt; #endif +#ifdef USE_WEBSOCKETS + struct ws_conn ws; +#endif } proto; struct connectbundle *bundle; /* The bundle we are member of */ @@ -115,12 +115,18 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) return result; } -CURLcode Curl_ws_accept(struct Curl_easy *data) +/* + * 'nread' is number of bytes of websocket data already in the buffer at + * 'mem'. + */ +CURLcode Curl_ws_accept(struct Curl_easy *data, + const char *mem, size_t nread) { struct SingleRequest *k = &data->req; struct HTTP *ws = data->req.p.http; struct connectdata *conn = data->conn; struct websocket *wsp = &data->req.p.http->ws; + struct ws_conn *wsc = &conn->proto.ws; CURLcode result; /* Verify the Sec-WebSocket-Accept response. @@ -149,13 +155,21 @@ CURLcode Curl_ws_accept(struct Curl_easy *data) infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x", ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]); + Curl_dyn_init(&wsc->early, data->set.buffer_size); + if(nread) { + result = Curl_dyn_addn(&wsc->early, mem, nread); + if(result) + return result; + infof(data, "%zu bytes websocket payload", nread); + wsp->stillb = Curl_dyn_ptr(&wsc->early); + wsp->stillblen = Curl_dyn_len(&wsc->early); + } k->upgr101 = UPGR101_RECEIVED; if(data->set.connect_only) /* switch off non-blocking sockets */ (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE); - wsp->oleft = 0; return result; } @@ -246,6 +260,9 @@ static CURLcode ws_decode(struct Curl_easy *data, infof(data, "WS: received OPCODE PONG"); *flags |= CURLWS_PONG; break; + default: + failf(data, "WS: unknown opcode: %x", opcode); + return CURLE_RECV_ERROR; } if(inbuf[1] & WSBIT_MASK) { @@ -419,15 +436,16 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, data->set.buffer_size, &n); if(result) return result; - if(!n) + if(!n) { /* connection closed */ + infof(data, "connection expectedly closed?"); return CURLE_GOT_NOTHING; + } wsp->stillb = data->state.buffer; wsp->stillblen = n; } - infof(data, "WS: got %u websocket bytes to decode", - (int)wsp->stillblen); + infof(data, "WS: %u bytes left to decode", (int)wsp->stillblen); if(!wsp->frame.bytesleft) { size_t headlen; curl_off_t oleft; @@ -439,6 +457,11 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, break; else if(result) return result; + if(datalen > buflen) { + size_t diff = datalen - buflen; + datalen = buflen; + oleft += diff; + } wsp->stillb += headlen; wsp->stillblen -= headlen; wsp->frame.offset = 0; @@ -450,7 +473,13 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, datalen = wsp->frame.bytesleft; if(datalen > wsp->stillblen) datalen = wsp->stillblen; + if(datalen > buflen) + datalen = buflen; + + wsp->frame.offset += wsp->frame.len; + wsp->frame.bytesleft -= datalen; } + wsp->frame.len = datalen; /* auto-respond to PINGs */ if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) { @@ -469,24 +498,17 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, wsp->stillblen -= nsent; } else if(datalen) { - /* there is payload for the user */ - if(datalen > buflen) - datalen = buflen; /* copy the payload to the user buffer */ memcpy(buffer, wsp->stillb, datalen); *nread = datalen; done = TRUE; - /* update buffer and frame info */ - wsp->frame.offset += datalen; - if(wsp->frame.bytesleft) - wsp->frame.bytesleft -= datalen; - DEBUGASSERT(datalen <= wsp->stillblen); wsp->stillblen -= datalen; if(wsp->stillblen) wsp->stillb += datalen; - else + else { wsp->stillb = NULL; + } } } *metap = &wsp->frame; @@ -724,8 +746,11 @@ CURLcode Curl_ws_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead_connection) { + struct ws_conn *wsc = &conn->proto.ws; (void)data; (void)dead_connection; + Curl_dyn_free(&wsc->early); + /* make sure this is non-blocking to avoid getting stuck in shutdown */ (void)curlx_nonblock(conn->sock[FIRSTSOCKET], TRUE); return CURLE_OK; @@ -46,27 +46,28 @@ struct websocket { size_t usedbuf; /* number of leading bytes in 'buf' the most recent complete websocket frame uses */ struct curl_ws_frame frame; /* the struct used for frame state */ - curl_off_t oleft; /* outstanding number of payload bytes left from the - server */ size_t stillblen; /* number of bytes left in the buffer to deliver in the next curl_ws_recv() call */ - char *stillb; /* the stillblen pending bytes are here */ + const char *stillb; /* the stillblen pending bytes are here */ curl_off_t sleft; /* outstanding number of payload bytes left to send */ unsigned int xori; /* xor index */ }; -CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); -CURLcode Curl_ws_accept(struct Curl_easy *data); +struct ws_conn { + struct dynbuf early; /* data already read when switching to ws */ +}; +CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); +CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp); void Curl_ws_done(struct Curl_easy *data); CURLcode Curl_ws_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead_connection); - #else #define Curl_ws_request(x,y) CURLE_OK #define Curl_ws_done(x) Curl_nop_stmt +#define Curl_ws_free(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_WS_H */ |