• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "curl_setup.h"
25 #include <curl/curl.h>
26 
27 #ifdef USE_WEBSOCKETS
28 
29 #include "urldata.h"
30 #include "dynbuf.h"
31 #include "rand.h"
32 #include "curl_base64.h"
33 #include "sendf.h"
34 #include "multiif.h"
35 #include "ws.h"
36 #include "easyif.h"
37 #include "transfer.h"
38 #include "nonblock.h"
39 
40 /* The last 3 #include files should be in this order */
41 #include "curl_printf.h"
42 #include "curl_memory.h"
43 #include "memdebug.h"
44 
45 struct wsfield {
46   const char *name;
47   const char *val;
48 };
49 
Curl_ws_request(struct Curl_easy * data,REQTYPE * req)50 CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
51 {
52   unsigned int i;
53   CURLcode result = CURLE_OK;
54   unsigned char rand[16];
55   char *randstr;
56   size_t randlen;
57   char keyval[40];
58   struct SingleRequest *k = &data->req;
59   struct wsfield heads[]= {
60     {
61       /* The request MUST contain an |Upgrade| header field whose value
62          MUST include the "websocket" keyword. */
63       "Upgrade:", "websocket"
64     },
65     {
66       /* The request MUST contain a |Connection| header field whose value
67          MUST include the "Upgrade" token. */
68       "Connection:", "Upgrade",
69     },
70     {
71       /* The request MUST include a header field with the name
72          |Sec-WebSocket-Version|. The value of this header field MUST be
73          13. */
74       "Sec-WebSocket-Version:", "13",
75     },
76     {
77       /* The request MUST include a header field with the name
78          |Sec-WebSocket-Key|. The value of this header field MUST be a nonce
79          consisting of a randomly selected 16-byte value that has been
80          base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be
81          selected randomly for each connection. */
82       "Sec-WebSocket-Key:", NULL,
83     }
84   };
85   heads[3].val = &keyval[0];
86 
87   /* 16 bytes random */
88   result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
89   if(result)
90     return result;
91   result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
92   if(result)
93     return result;
94   DEBUGASSERT(randlen < sizeof(keyval));
95   if(randlen >= sizeof(keyval))
96     return CURLE_FAILED_INIT;
97   strcpy(keyval, randstr);
98   free(randstr);
99   for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) {
100     if(!Curl_checkheaders(data, STRCONST(heads[i].name))) {
101 #ifdef USE_HYPER
102       char field[128];
103       msnprintf(field, sizeof(field), "%s %s", heads[i].name,
104                 heads[i].val);
105       result = Curl_hyper_header(data, req, field);
106 #else
107       (void)data;
108       result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name,
109                              heads[i].val);
110 #endif
111     }
112   }
113   k->upgr101 = UPGR101_WS;
114   Curl_dyn_init(&data->req.p.http->ws.buf, MAX_WS_SIZE * 2);
115   return result;
116 }
117 
118 /*
119  * 'nread' is number of bytes of websocket data already in the buffer at
120  * 'mem'.
121  */
Curl_ws_accept(struct Curl_easy * data,const char * mem,size_t nread)122 CURLcode Curl_ws_accept(struct Curl_easy *data,
123                         const char *mem, size_t nread)
124 {
125   struct SingleRequest *k = &data->req;
126   struct HTTP *ws = data->req.p.http;
127   struct connectdata *conn = data->conn;
128   struct websocket *wsp = &data->req.p.http->ws;
129   struct ws_conn *wsc = &conn->proto.ws;
130   CURLcode result;
131 
132   /* Verify the Sec-WebSocket-Accept response.
133 
134      The sent value is the base64 encoded version of a SHA-1 hash done on the
135      |Sec-WebSocket-Key| header field concatenated with
136      the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
137   */
138 
139   /* If the response includes a |Sec-WebSocket-Extensions| header field and
140      this header field indicates the use of an extension that was not present
141      in the client's handshake (the server has indicated an extension not
142      requested by the client), the client MUST Fail the WebSocket Connection.
143   */
144 
145   /* If the response includes a |Sec-WebSocket-Protocol| header field
146      and this header field indicates the use of a subprotocol that was
147      not present in the client's handshake (the server has indicated a
148      subprotocol not requested by the client), the client MUST Fail
149      the WebSocket Connection. */
150 
151   /* 4 bytes random */
152   result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask));
153   if(result)
154     return result;
155 
156   infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
157         ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
158   Curl_dyn_init(&wsc->early, data->set.buffer_size);
159   if(nread) {
160     result = Curl_dyn_addn(&wsc->early, mem, nread);
161     if(result)
162       return result;
163     infof(data, "%zu bytes websocket payload", nread);
164     wsp->stillb = Curl_dyn_ptr(&wsc->early);
165     wsp->stillblen = Curl_dyn_len(&wsc->early);
166   }
167   k->upgr101 = UPGR101_RECEIVED;
168 
169   return result;
170 }
171 
172 #define WSBIT_FIN 0x80
173 #define WSBIT_OPCODE_CONT  0
174 #define WSBIT_OPCODE_TEXT  (1)
175 #define WSBIT_OPCODE_BIN   (2)
176 #define WSBIT_OPCODE_CLOSE (8)
177 #define WSBIT_OPCODE_PING  (9)
178 #define WSBIT_OPCODE_PONG  (0xa)
179 #define WSBIT_OPCODE_MASK  (0xf)
180 
181 #define WSBIT_MASK 0x80
182 
183 /* remove the spent bytes from the beginning of the buffer as that part has
184    now been delivered to the application */
ws_decode_shift(struct Curl_easy * data,size_t spent)185 static void ws_decode_shift(struct Curl_easy *data, size_t spent)
186 {
187   struct websocket *wsp = &data->req.p.http->ws;
188   size_t len = Curl_dyn_len(&wsp->buf);
189   size_t keep = len - spent;
190   DEBUGASSERT(len >= spent);
191   Curl_dyn_tail(&wsp->buf, keep);
192 }
193 
194 /* ws_decode() decodes a binary frame into structured WebSocket data,
195 
196    data - the transfer
197    inbuf - incoming raw data. If NULL, work on the already buffered data.
198    inlen - size of the provided data, perhaps too little, perhaps too much
199    headlen - stored length of the frame header
200    olen - stored length of the extracted data
201    oleft - number of unread bytes pending to that belongs to this frame
202    flags - stored bitmask about the frame
203 
204    Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
205    stores the first part in the ->extra buffer to be used in the next call
206    when more data is provided.
207 */
208 
ws_decode(struct Curl_easy * data,unsigned char * inbuf,size_t inlen,size_t * headlen,size_t * olen,curl_off_t * oleft,unsigned int * flags)209 static CURLcode ws_decode(struct Curl_easy *data,
210                           unsigned char *inbuf, size_t inlen,
211                           size_t *headlen, size_t *olen,
212                           curl_off_t *oleft,
213                           unsigned int *flags)
214 {
215   bool fin;
216   unsigned char opcode;
217   curl_off_t total;
218   size_t dataindex = 2;
219   curl_off_t payloadsize;
220 
221   *olen = *headlen = 0;
222 
223   if(inlen < 2) {
224     /* the smallest possible frame is two bytes */
225     infof(data, "WS: plen == %u, EAGAIN", (int)inlen);
226     return CURLE_AGAIN;
227   }
228 
229   fin = inbuf[0] & WSBIT_FIN;
230   opcode = inbuf[0] & WSBIT_OPCODE_MASK;
231   infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin);
232   *flags = 0;
233   switch(opcode) {
234   case WSBIT_OPCODE_CONT:
235     if(!fin)
236       *flags |= CURLWS_CONT;
237     infof(data, "WS: received OPCODE CONT");
238     break;
239   case WSBIT_OPCODE_TEXT:
240     infof(data, "WS: received OPCODE TEXT");
241     *flags |= CURLWS_TEXT;
242     break;
243   case WSBIT_OPCODE_BIN:
244     infof(data, "WS: received OPCODE BINARY");
245     *flags |= CURLWS_BINARY;
246     break;
247   case WSBIT_OPCODE_CLOSE:
248     infof(data, "WS: received OPCODE CLOSE");
249     *flags |= CURLWS_CLOSE;
250     break;
251   case WSBIT_OPCODE_PING:
252     infof(data, "WS: received OPCODE PING");
253     *flags |= CURLWS_PING;
254     break;
255   case WSBIT_OPCODE_PONG:
256     infof(data, "WS: received OPCODE PONG");
257     *flags |= CURLWS_PONG;
258     break;
259   default:
260     failf(data, "WS: unknown opcode: %x", opcode);
261     return CURLE_RECV_ERROR;
262   }
263 
264   if(inbuf[1] & WSBIT_MASK) {
265     /* A client MUST close a connection if it detects a masked frame. */
266     failf(data, "WS: masked input frame");
267     return CURLE_RECV_ERROR;
268   }
269   payloadsize = inbuf[1];
270   if(payloadsize == 126) {
271     if(inlen < 4) {
272       infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)inlen);
273       return CURLE_AGAIN; /* not enough data available */
274     }
275     payloadsize = (inbuf[2] << 8) | inbuf[3];
276     dataindex += 2;
277   }
278   else if(payloadsize == 127) {
279     /* 64 bit payload size */
280     if(inlen < 10)
281       return CURLE_AGAIN;
282     if(inbuf[2] & 80) {
283       failf(data, "WS: too large frame");
284       return CURLE_RECV_ERROR;
285     }
286     dataindex += 8;
287     payloadsize = ((curl_off_t)inbuf[2] << 56) |
288       (curl_off_t)inbuf[3] << 48 |
289       (curl_off_t)inbuf[4] << 40 |
290       (curl_off_t)inbuf[5] << 32 |
291       (curl_off_t)inbuf[6] << 24 |
292       (curl_off_t)inbuf[7] << 16 |
293       (curl_off_t)inbuf[8] << 8 |
294       inbuf[9];
295   }
296 
297   /* point to the payload */
298   *headlen = dataindex;
299   total = dataindex + payloadsize;
300   if(total > (curl_off_t)inlen) {
301     /* buffer contains partial frame */
302     *olen = inlen - dataindex; /* bytes to write out */
303     *oleft = total - inlen;    /* bytes yet to come (for this frame) */
304     payloadsize = total - dataindex;
305   }
306   else {
307     /* we have the complete frame (`total` bytes) in buffer */
308     *olen = payloadsize;    /* bytes to write out */
309     *oleft = 0;             /* bytes yet to come (for this frame) */
310   }
311 
312   infof(data, "WS: received %Ou bytes payload (%Ou left, buflen was %zu)",
313         payloadsize, *oleft, inlen);
314   return CURLE_OK;
315 }
316 
317 /* Curl_ws_writecb() is the write callback for websocket traffic. The
318    websocket data is provided to this raw, in chunks. This function should
319    handle/decode the data and call the "real" underlying callback accordingly.
320 */
Curl_ws_writecb(char * buffer,size_t size,size_t nitems,void * userp)321 size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
322                        size_t nitems, void *userp)
323 {
324   struct HTTP *ws = (struct HTTP *)userp;
325   struct Curl_easy *data = ws->ws.data;
326   struct websocket *wsp = &data->req.p.http->ws;
327   void *writebody_ptr = data->set.out;
328   if(data->set.ws_raw_mode)
329     return data->set.fwrite_func(buffer, size, nitems, writebody_ptr);
330   else if(nitems) {
331     size_t wrote = 0, headlen;
332     CURLcode result;
333 
334     if(buffer) {
335       result = Curl_dyn_addn(&wsp->buf, buffer, nitems);
336       if(result) {
337         infof(data, "WS: error adding data to buffer %d", (int)result);
338         return nitems - 1;
339       }
340       buffer = NULL;
341     }
342 
343     while(Curl_dyn_len(&wsp->buf)) {
344       unsigned char *wsbuf = Curl_dyn_uptr(&wsp->buf);
345       size_t buflen = Curl_dyn_len(&wsp->buf);
346       size_t write_len = 0;
347       size_t consumed = 0;
348 
349       if(!ws->ws.frame.bytesleft) {
350         unsigned int recvflags;
351         curl_off_t fb_left;
352 
353         result = ws_decode(data, wsbuf, buflen,
354                            &headlen, &write_len, &fb_left, &recvflags);
355         if(result == CURLE_AGAIN)
356           /* insufficient amount of data, keep it for later.
357            * we pretend to have written all since we have a copy */
358           return nitems;
359         else if(result) {
360           infof(data, "WS: decode error %d", (int)result);
361           return nitems - 1;
362         }
363         consumed += headlen;
364         wsbuf += headlen;
365         buflen -= headlen;
366 
367         /* New frame. store details about the frame to be reachable with
368            curl_ws_meta() from within the write callback */
369         ws->ws.frame.age = 0;
370         ws->ws.frame.offset = 0;
371         ws->ws.frame.flags = recvflags;
372         ws->ws.frame.bytesleft = fb_left;
373       }
374       else {
375         /* continuing frame */
376         write_len = (size_t)ws->ws.frame.bytesleft;
377         if(write_len > buflen)
378           write_len = buflen;
379         ws->ws.frame.offset += write_len;
380         ws->ws.frame.bytesleft -= write_len;
381       }
382       if((ws->ws.frame.flags & CURLWS_PING) && !ws->ws.frame.bytesleft) {
383         /* auto-respond to PINGs, only works for single-frame payloads atm */
384         size_t bytes;
385         infof(data, "WS: auto-respond to PING with a PONG");
386         /* send back the exact same content as a PONG */
387         result = curl_ws_send(data, wsbuf, write_len,
388                               &bytes, 0, CURLWS_PONG);
389         if(result)
390           return result;
391       }
392       else if(write_len || !wsp->frame.bytesleft) {
393         /* deliver the decoded frame to the user callback */
394         Curl_set_in_callback(data, true);
395         wrote = data->set.fwrite_func((char *)wsbuf, 1,
396                                       write_len, writebody_ptr);
397         Curl_set_in_callback(data, false);
398         if(wrote != write_len)
399           return 0;
400       }
401       /* get rid of the buffered data consumed */
402       consumed += write_len;
403       ws_decode_shift(data, consumed);
404     }
405   }
406   return nitems;
407 }
408 
curl_ws_recv(struct Curl_easy * data,void * buffer,size_t buflen,size_t * nread,struct curl_ws_frame ** metap)409 CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
410                                   size_t buflen, size_t *nread,
411                                   struct curl_ws_frame **metap)
412 {
413   CURLcode result;
414   struct websocket *wsp = &data->req.p.http->ws;
415   bool done = FALSE; /* not filled passed buffer yet */
416 
417   *nread = 0;
418   *metap = NULL;
419   /* get a download buffer */
420   result = Curl_preconnect(data);
421   if(result)
422     return result;
423 
424   while(!done) {
425     size_t datalen;
426     unsigned int recvflags;
427 
428     if(!wsp->stillblen) {
429       /* try to get more data */
430       size_t n;
431       result = curl_easy_recv(data, data->state.buffer,
432                               data->set.buffer_size, &n);
433       if(result)
434         return result;
435       if(!n) {
436         /* connection closed */
437         infof(data, "connection expectedly closed?");
438         return CURLE_GOT_NOTHING;
439       }
440       wsp->stillb = data->state.buffer;
441       wsp->stillblen = n;
442     }
443 
444     infof(data, "WS: %u bytes left to decode", (int)wsp->stillblen);
445     if(!wsp->frame.bytesleft) {
446       size_t headlen;
447       curl_off_t oleft;
448       /* detect new frame */
449       result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen,
450                          &headlen, &datalen, &oleft, &recvflags);
451       if(result == CURLE_AGAIN)
452         /* a packet fragment only */
453         break;
454       else if(result)
455         return result;
456       if(datalen > buflen) {
457         size_t diff = datalen - buflen;
458         datalen = buflen;
459         oleft += diff;
460       }
461       wsp->stillb += headlen;
462       wsp->stillblen -= headlen;
463       wsp->frame.offset = 0;
464       wsp->frame.bytesleft = oleft;
465       wsp->frame.flags = recvflags;
466     }
467     else {
468       /* existing frame, remaining payload handling */
469       datalen = wsp->frame.bytesleft;
470       if(datalen > wsp->stillblen)
471         datalen = wsp->stillblen;
472       if(datalen > buflen)
473         datalen = buflen;
474 
475       wsp->frame.offset += wsp->frame.len;
476       wsp->frame.bytesleft -= datalen;
477     }
478     wsp->frame.len = datalen;
479 
480     /* auto-respond to PINGs */
481     if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) {
482       size_t nsent = 0;
483       infof(data, "WS: auto-respond to PING with a PONG, %zu bytes payload",
484             datalen);
485       /* send back the exact same content as a PONG */
486       result = curl_ws_send(data, wsp->stillb, datalen, &nsent, 0,
487                             CURLWS_PONG);
488       if(result)
489         return result;
490       infof(data, "WS: bytesleft %zu datalen %zu",
491             wsp->frame.bytesleft, datalen);
492       /* we handled the data part of the PING, advance over that */
493       wsp->stillb += nsent;
494       wsp->stillblen -= nsent;
495     }
496     else if(datalen) {
497       /* copy the payload to the user buffer */
498       memcpy(buffer, wsp->stillb, datalen);
499       *nread = datalen;
500       done = TRUE;
501 
502       wsp->stillblen -= datalen;
503       if(wsp->stillblen)
504         wsp->stillb += datalen;
505       else {
506         wsp->stillb = NULL;
507       }
508     }
509   }
510   *metap = &wsp->frame;
511   return CURLE_OK;
512 }
513 
ws_xor(struct Curl_easy * data,const unsigned char * source,unsigned char * dest,size_t len)514 static void ws_xor(struct Curl_easy *data,
515                    const unsigned char *source,
516                    unsigned char *dest,
517                    size_t len)
518 {
519   struct websocket *wsp = &data->req.p.http->ws;
520   size_t i;
521   /* append payload after the mask, XOR appropriately */
522   for(i = 0; i < len; i++) {
523     dest[i] = source[i] ^ wsp->mask[wsp->xori];
524     wsp->xori++;
525     wsp->xori &= 3;
526   }
527 }
528 
529 /***
530     RFC 6455 Section 5.2
531 
532       0                   1                   2                   3
533       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
534      +-+-+-+-+-------+-+-------------+-------------------------------+
535      |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
536      |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
537      |N|V|V|V|       |S|             |   (if payload len==126/127)   |
538      | |1|2|3|       |K|             |                               |
539      +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
540      |     Extended payload length continued, if payload len == 127  |
541      + - - - - - - - - - - - - - - - +-------------------------------+
542      |                               |Masking-key, if MASK set to 1  |
543      +-------------------------------+-------------------------------+
544      | Masking-key (continued)       |          Payload Data         |
545      +-------------------------------- - - - - - - - - - - - - - - - +
546      :                     Payload Data continued ...                :
547      + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
548      |                     Payload Data continued ...                |
549      +---------------------------------------------------------------+
550 */
551 
ws_packethead(struct Curl_easy * data,size_t len,unsigned int flags)552 static size_t ws_packethead(struct Curl_easy *data,
553                             size_t len, unsigned int flags)
554 {
555   struct HTTP *ws = data->req.p.http;
556   unsigned char *out = (unsigned char *)data->state.ulbuf;
557   unsigned char firstbyte = 0;
558   int outi;
559   unsigned char opcode;
560   if(flags & CURLWS_TEXT) {
561     opcode = WSBIT_OPCODE_TEXT;
562     infof(data, "WS: send OPCODE TEXT");
563   }
564   else if(flags & CURLWS_CLOSE) {
565     opcode = WSBIT_OPCODE_CLOSE;
566     infof(data, "WS: send OPCODE CLOSE");
567   }
568   else if(flags & CURLWS_PING) {
569     opcode = WSBIT_OPCODE_PING;
570     infof(data, "WS: send OPCODE PING");
571   }
572   else if(flags & CURLWS_PONG) {
573     opcode = WSBIT_OPCODE_PONG;
574     infof(data, "WS: send OPCODE PONG");
575   }
576   else {
577     opcode = WSBIT_OPCODE_BIN;
578     infof(data, "WS: send OPCODE BINARY");
579   }
580 
581   if(!(flags & CURLWS_CONT)) {
582     if(!ws->ws.contfragment)
583       /* not marked as continuing, this is the final fragment */
584       firstbyte |= WSBIT_FIN | opcode;
585     else
586       /* marked as continuing, this is the final fragment; set CONT
587          opcode and FIN bit */
588       firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT;
589 
590     ws->ws.contfragment = FALSE;
591     infof(data, "WS: set FIN");
592   }
593   else if(ws->ws.contfragment) {
594     /* the previous fragment was not a final one and this isn't either, keep a
595        CONT opcode and no FIN bit */
596     firstbyte |= WSBIT_OPCODE_CONT;
597     infof(data, "WS: keep CONT, no FIN");
598   }
599   else {
600     firstbyte = opcode;
601     ws->ws.contfragment = TRUE;
602     infof(data, "WS: set CONT, no FIN");
603   }
604   out[0] = firstbyte;
605   if(len > 65535) {
606     out[1] = 127 | WSBIT_MASK;
607     out[2] = (len >> 8) & 0xff;
608     out[3] = len & 0xff;
609     outi = 10;
610   }
611   else if(len > 126) {
612     out[1] = 126 | WSBIT_MASK;
613     out[2] = (len >> 8) & 0xff;
614     out[3] = len & 0xff;
615     outi = 4;
616   }
617   else {
618     out[1] = (unsigned char)len | WSBIT_MASK;
619     outi = 2;
620   }
621 
622   infof(data, "WS: send FIN bit %u (byte %02x)",
623         firstbyte & WSBIT_FIN ? 1 : 0,
624         firstbyte);
625   infof(data, "WS: send payload len %u", (int)len);
626 
627   /* 4 bytes mask */
628   memcpy(&out[outi], &ws->ws.mask, 4);
629 
630   if(data->set.upload_buffer_size < (len + 10))
631     return 0;
632 
633   /* pass over the mask */
634   outi += 4;
635 
636   ws->ws.xori = 0;
637   /* return packet size */
638   return outi;
639 }
640 
curl_ws_send(struct Curl_easy * data,const void * buffer,size_t buflen,size_t * sent,curl_off_t totalsize,unsigned int sendflags)641 CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
642                                   size_t buflen, size_t *sent,
643                                   curl_off_t totalsize,
644                                   unsigned int sendflags)
645 {
646   CURLcode result;
647   size_t headlen;
648   char *out;
649   ssize_t written;
650   struct websocket *wsp = &data->req.p.http->ws;
651 
652   if(!data->set.ws_raw_mode) {
653     result = Curl_get_upload_buffer(data);
654     if(result)
655       return result;
656   }
657   else {
658     if(totalsize || sendflags)
659       return CURLE_BAD_FUNCTION_ARGUMENT;
660   }
661 
662   if(data->set.ws_raw_mode) {
663     if(!buflen)
664       /* nothing to do */
665       return CURLE_OK;
666     /* raw mode sends exactly what was requested, and this is from within
667        the write callback */
668     if(Curl_is_in_callback(data)) {
669       if(!data->conn) {
670         failf(data, "No associated connection");
671         return CURLE_SEND_ERROR;
672       }
673       result = Curl_write(data, data->conn->writesockfd, buffer, buflen,
674                           &written);
675     }
676     else
677       result = Curl_senddata(data, buffer, buflen, &written);
678 
679     infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
680           buflen, written);
681     *sent = written;
682     return result;
683   }
684 
685   if(buflen > (data->set.upload_buffer_size - 10))
686     /* don't do more than this in one go */
687     buflen = data->set.upload_buffer_size - 10;
688 
689   if(sendflags & CURLWS_OFFSET) {
690     if(totalsize) {
691       /* a frame series 'totalsize' bytes big, this is the first */
692       headlen = ws_packethead(data, totalsize, sendflags);
693       wsp->sleft = totalsize - buflen;
694     }
695     else {
696       headlen = 0;
697       if((curl_off_t)buflen > wsp->sleft) {
698         infof(data, "WS: unaligned frame size (sending %zu instead of %zu)",
699               buflen, wsp->sleft);
700         wsp->sleft = 0;
701       }
702       else
703         wsp->sleft -= buflen;
704     }
705   }
706   else
707     headlen = ws_packethead(data, buflen, sendflags);
708 
709   /* headlen is the size of the frame header */
710   out = data->state.ulbuf;
711   if(buflen)
712     /* for PING and PONG etc there might not be a payload */
713     ws_xor(data, buffer, (unsigned char *)out + headlen, buflen);
714 
715   if(data->set.connect_only)
716     result = Curl_senddata(data, out, buflen + headlen, &written);
717   else
718     result = Curl_write(data, data->conn->writesockfd, out,
719                         buflen + headlen, &written);
720 
721   infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
722         headlen + buflen, written);
723 
724   if(!result) {
725     /* the *sent number only counts "payload", excluding the header */
726     if((size_t)written > headlen)
727       *sent = written - headlen;
728     else
729       *sent = 0;
730   }
731   return result;
732 }
733 
Curl_ws_done(struct Curl_easy * data)734 void Curl_ws_done(struct Curl_easy *data)
735 {
736   struct websocket *wsp = &data->req.p.http->ws;
737   DEBUGASSERT(wsp);
738   Curl_dyn_free(&wsp->buf);
739 }
740 
Curl_ws_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)741 CURLcode Curl_ws_disconnect(struct Curl_easy *data,
742                             struct connectdata *conn,
743                             bool dead_connection)
744 {
745   struct ws_conn *wsc = &conn->proto.ws;
746   (void)data;
747   (void)dead_connection;
748   Curl_dyn_free(&wsc->early);
749   return CURLE_OK;
750 }
751 
curl_ws_meta(struct Curl_easy * data)752 CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
753 {
754   /* we only return something for websocket, called from within the callback
755      when not using raw mode */
756   if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http &&
757      !data->set.ws_raw_mode)
758     return &data->req.p.http->ws.frame;
759   return NULL;
760 }
761 
762 #else
763 
curl_ws_recv(CURL * curl,void * buffer,size_t buflen,size_t * nread,struct curl_ws_frame ** metap)764 CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
765                                   size_t *nread,
766                                   struct curl_ws_frame **metap)
767 {
768   (void)curl;
769   (void)buffer;
770   (void)buflen;
771   (void)nread;
772   (void)metap;
773   return CURLE_NOT_BUILT_IN;
774 }
775 
curl_ws_send(CURL * curl,const void * buffer,size_t buflen,size_t * sent,curl_off_t framesize,unsigned int sendflags)776 CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
777                                   size_t buflen, size_t *sent,
778                                   curl_off_t framesize,
779                                   unsigned int sendflags)
780 {
781   (void)curl;
782   (void)buffer;
783   (void)buflen;
784   (void)sent;
785   (void)framesize;
786   (void)sendflags;
787   return CURLE_NOT_BUILT_IN;
788 }
789 
curl_ws_meta(struct Curl_easy * data)790 CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
791 {
792   (void)data;
793   return NULL;
794 }
795 #endif /* USE_WEBSOCKETS */
796