Browse Source

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
Daniel Stenberg 1 year ago
parent
commit
2e2e3d16c5
6 changed files with 52 additions and 23 deletions
  1. 1 1
      lib/c-hyper.c
  2. 1 1
      lib/easy.c
  3. 1 1
      lib/http.c
  4. 3 0
      lib/urldata.h
  5. 39 14
      lib/ws.c
  6. 7 6
      lib/ws.h

+ 1 - 1
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;
       }

+ 1 - 1
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;
 }
 

+ 1 - 1
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! */

+ 3 - 0
lib/urldata.h

@@ -1014,6 +1014,9 @@ struct connectdata {
     struct ldapconninfo *ldapc;
 #ifndef CURL_DISABLE_MQTT
     struct mqtt_conn mqtt;
+#endif
+#ifdef USE_WEBSOCKETS
+    struct ws_conn ws;
 #endif
   } proto;
 

+ 39 - 14
lib/ws.c

@@ -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;

+ 7 - 6
lib/ws.h

@@ -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 */