Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / ws.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2022, 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
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 CURLcode Curl_ws_accept(struct Curl_easy *data)
119 {
120   struct SingleRequest *k = &data->req;
121   struct HTTP *ws = data->req.p.http;
122   struct connectdata *conn = data->conn;
123   struct websocket *wsp = &data->req.p.http->ws;
124   CURLcode result;
125
126   /* Verify the Sec-WebSocket-Accept response.
127
128      The sent value is the base64 encoded version of a SHA-1 hash done on the
129      |Sec-WebSocket-Key| header field concatenated with
130      the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
131   */
132
133   /* If the response includes a |Sec-WebSocket-Extensions| header field and
134      this header field indicates the use of an extension that was not present
135      in the client's handshake (the server has indicated an extension not
136      requested by the client), the client MUST Fail the WebSocket Connection.
137   */
138
139   /* If the response includes a |Sec-WebSocket-Protocol| header field
140      and this header field indicates the use of a subprotocol that was
141      not present in the client's handshake (the server has indicated a
142      subprotocol not requested by the client), the client MUST Fail
143      the WebSocket Connection. */
144
145   /* 4 bytes random */
146   result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask));
147   if(result)
148     return result;
149
150   infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
151         ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
152   k->upgr101 = UPGR101_RECEIVED;
153
154   if(data->set.connect_only)
155     /* switch off non-blocking sockets */
156     (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE);
157
158   wsp->oleft = 0;
159   return result;
160 }
161
162 #define WSBIT_FIN 0x80
163 #define WSBIT_OPCODE_CONT  0
164 #define WSBIT_OPCODE_TEXT  (1)
165 #define WSBIT_OPCODE_BIN   (2)
166 #define WSBIT_OPCODE_CLOSE (8)
167 #define WSBIT_OPCODE_PING  (9)
168 #define WSBIT_OPCODE_PONG  (0xa)
169 #define WSBIT_OPCODE_MASK  (0xf)
170
171 #define WSBIT_MASK 0x80
172
173 /* remove the spent bytes from the beginning of the buffer as that part has
174    now been delivered to the application */
175 static void ws_decode_clear(struct Curl_easy *data)
176 {
177   struct websocket *wsp = &data->req.p.http->ws;
178   size_t spent = wsp->usedbuf;
179   size_t len = Curl_dyn_len(&wsp->buf);
180   size_t keep = len - spent;
181   DEBUGASSERT(len >= spent);
182   Curl_dyn_tail(&wsp->buf, keep);
183 }
184
185 /* ws_decode() decodes a binary frame into structured WebSocket data,
186
187    wpkt - the incoming raw data. If NULL, work on the already buffered data.
188    ilen - the size of the provided data, perhaps too little, perhaps too much
189    out - stored pointed to extracted data
190    olen - stored length of the extracted data
191    oleft - number of unread bytes pending to that belongs to this frame
192    more - if there is more data in there
193    flags - stored bitmask about the frame
194
195    Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
196    stores the first part in the ->extra buffer to be used in the next call
197    when more data is provided.
198 */
199
200 static CURLcode ws_decode(struct Curl_easy *data,
201                           unsigned char *wpkt, size_t ilen,
202                           unsigned char **out, size_t *olen,
203                           curl_off_t *oleft,
204                           bool *more,
205                           unsigned int *flags)
206 {
207   bool fin;
208   unsigned char opcode;
209   curl_off_t total;
210   size_t dataindex = 2;
211   curl_off_t plen; /* size of data in the buffer */
212   curl_off_t payloadsize;
213   struct websocket *wsp = &data->req.p.http->ws;
214   unsigned char *p;
215   CURLcode result;
216
217   *olen = 0;
218
219   /* add the incoming bytes, if any */
220   if(wpkt) {
221     result = Curl_dyn_addn(&wsp->buf, wpkt, ilen);
222     if(result)
223       return result;
224   }
225
226   plen = Curl_dyn_len(&wsp->buf);
227   if(plen < 2) {
228     /* the smallest possible frame is two bytes */
229     infof(data, "WS: plen == %u, EAGAIN", (int)plen);
230     return CURLE_AGAIN;
231   }
232
233   p = Curl_dyn_uptr(&wsp->buf);
234
235   fin = p[0] & WSBIT_FIN;
236   opcode = p[0] & WSBIT_OPCODE_MASK;
237   infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin);
238   *flags = 0;
239   switch(opcode) {
240   case WSBIT_OPCODE_CONT:
241     if(!fin)
242       *flags |= CURLWS_CONT;
243     infof(data, "WS: received OPCODE CONT");
244     break;
245   case WSBIT_OPCODE_TEXT:
246     infof(data, "WS: received OPCODE TEXT");
247     *flags |= CURLWS_TEXT;
248     break;
249   case WSBIT_OPCODE_BIN:
250     infof(data, "WS: received OPCODE BINARY");
251     *flags |= CURLWS_BINARY;
252     break;
253   case WSBIT_OPCODE_CLOSE:
254     infof(data, "WS: received OPCODE CLOSE");
255     *flags |= CURLWS_CLOSE;
256     break;
257   case WSBIT_OPCODE_PING:
258     infof(data, "WS: received OPCODE PING");
259     *flags |= CURLWS_PING;
260     break;
261   case WSBIT_OPCODE_PONG:
262     infof(data, "WS: received OPCODE PONG");
263     *flags |= CURLWS_PONG;
264     break;
265   }
266
267   if(p[1] & WSBIT_MASK) {
268     /* A client MUST close a connection if it detects a masked frame. */
269     failf(data, "WS: masked input frame");
270     return CURLE_RECV_ERROR;
271   }
272   payloadsize = p[1];
273   if(payloadsize == 126) {
274     if(plen < 4) {
275       infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)plen);
276       return CURLE_AGAIN; /* not enough data available */
277     }
278     payloadsize = (p[2] << 8) | p[3];
279     dataindex += 2;
280   }
281   else if(payloadsize == 127) {
282     /* 64 bit payload size */
283     if(plen < 10)
284       return CURLE_AGAIN;
285     if(p[2] & 80) {
286       failf(data, "WS: too large frame");
287       return CURLE_RECV_ERROR;
288     }
289     dataindex += 8;
290     payloadsize = ((curl_off_t)p[2] << 56) |
291       (curl_off_t)p[3] << 48 |
292       (curl_off_t)p[4] << 40 |
293       (curl_off_t)p[5] << 32 |
294       (curl_off_t)p[6] << 24 |
295       (curl_off_t)p[7] << 16 |
296       (curl_off_t)p[8] << 8 |
297       p[9];
298   }
299
300   total = dataindex + payloadsize;
301   if(total > plen) {
302     /* deliver a partial frame */
303     *oleft = total - dataindex;
304     payloadsize = total - dataindex;
305   }
306   else {
307     *oleft = 0;
308     if(plen > total)
309       /* there is another fragment after */
310       *more = TRUE;
311   }
312
313   /* point to the payload */
314   *out = &p[dataindex];
315
316   /* return the payload length */
317   *olen = payloadsize;
318
319   /* number of bytes "used" from the buffer */
320   wsp->usedbuf = dataindex + payloadsize;
321   infof(data, "WS: received %zu bytes payload (%zu left)",
322         payloadsize, *oleft);
323   return CURLE_OK;
324 }
325
326 /* Curl_ws_writecb() is the write callback for websocket traffic. The
327    websocket data is provided to this raw, in chunks. This function should
328    handle/decode the data and call the "real" underlying callback accordingly.
329 */
330 size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
331                        size_t nitems, void *userp)
332 {
333   struct HTTP *ws = (struct HTTP *)userp;
334   struct Curl_easy *data = ws->ws.data;
335   void *writebody_ptr = data->set.out;
336   if(data->set.ws_raw_mode)
337     return data->set.fwrite_func(buffer, size, nitems, writebody_ptr);
338   else if(nitems) {
339     unsigned char *frame = NULL;
340     size_t flen = 0;
341     size_t wrote = 0;
342     CURLcode result;
343     bool more; /* there's is more to parse in the buffer */
344     curl_off_t oleft;
345
346     decode:
347     more = FALSE;
348     oleft = ws->ws.frame.bytesleft;
349     if(!oleft) {
350       unsigned int recvflags;
351       result = ws_decode(data, (unsigned char *)buffer, nitems,
352                          &frame, &flen, &oleft, &more, &recvflags);
353       if(result == CURLE_AGAIN)
354         /* insufficient amount of data, keep it for later */
355         return nitems;
356       else if(result) {
357         infof(data, "WS: decode error %d", (int)result);
358         return nitems - 1;
359       }
360       /* Store details about the frame to be reachable with curl_ws_meta()
361          from within the write callback */
362       ws->ws.frame.age = 0;
363       ws->ws.frame.offset = 0;
364       ws->ws.frame.flags = recvflags;
365       ws->ws.frame.bytesleft = oleft;
366     }
367     else {
368       if(nitems > (size_t)ws->ws.frame.bytesleft) {
369         nitems = ws->ws.frame.bytesleft;
370         more = TRUE;
371       }
372       else
373         more = FALSE;
374       ws->ws.frame.offset += nitems;
375       ws->ws.frame.bytesleft -= nitems;
376       frame = (unsigned char *)buffer;
377       flen = nitems;
378     }
379     if((ws->ws.frame.flags & CURLWS_PING) && !oleft) {
380       /* auto-respond to PINGs, only works for single-frame payloads atm */
381       size_t bytes;
382       infof(data, "WS: auto-respond to PING with a PONG");
383       DEBUGASSERT(frame);
384       /* send back the exact same content as a PONG */
385       result = curl_ws_send(data, frame, flen, &bytes, 0, CURLWS_PONG);
386       if(result)
387         return result;
388     }
389     else {
390       /* deliver the decoded frame to the user callback */
391       Curl_set_in_callback(data, true);
392       wrote = data->set.fwrite_func((char *)frame, 1, flen, writebody_ptr);
393       Curl_set_in_callback(data, false);
394       if(wrote != flen)
395         return 0;
396     }
397     if(oleft)
398       ws->ws.frame.offset += flen;
399     /* the websocket frame has been delivered */
400     ws_decode_clear(data);
401     if(more) {
402       /* there's more websocket data to deal with in the buffer */
403       buffer = NULL; /* the buffer as been drained already */
404       goto decode;
405     }
406   }
407   return nitems;
408 }
409
410
411 CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
412                                   size_t buflen, size_t *nread,
413                                   struct curl_ws_frame **metap)
414 {
415   size_t bytes;
416   CURLcode result;
417   struct websocket *wsp = &data->req.p.http->ws;
418
419   *nread = 0;
420   *metap = NULL;
421   /* get a download buffer */
422   result = Curl_preconnect(data);
423   if(result)
424     return result;
425
426   do {
427     bool drain = FALSE; /* if there is pending buffered data to drain */
428     char *inbuf = data->state.buffer;
429     bytes = wsp->stillbuffer;
430     if(!bytes) {
431       result = curl_easy_recv(data, data->state.buffer,
432                               data->set.buffer_size, &bytes);
433       if(result)
434         return result;
435     }
436     else {
437       /* the pending bytes can be found here */
438       inbuf = wsp->stillb;
439       drain = TRUE;
440     }
441     if(bytes) {
442       unsigned char *out;
443       size_t olen;
444       bool more;
445       unsigned int recvflags;
446       curl_off_t oleft = wsp->frame.bytesleft;
447
448       infof(data, "WS: got %u websocket bytes to decode", (int)bytes);
449       if(!oleft && !drain) {
450         result = ws_decode(data, (unsigned char *)inbuf, bytes,
451                            &out, &olen, &oleft, &more, &recvflags);
452         if(result == CURLE_AGAIN)
453           /* a packet fragment only */
454           break;
455         else if(result)
456           return result;
457         wsp->frame.offset = 0;
458         wsp->frame.bytesleft = oleft;
459         wsp->frame.flags = recvflags;
460       }
461       else {
462         olen = oleft;
463         out = (unsigned char *)wsp->stillb;
464         recvflags = wsp->frame.flags;
465         if((curl_off_t)buflen < oleft)
466           /* there is still data left after this */
467           wsp->frame.bytesleft -= buflen;
468         else
469           wsp->frame.bytesleft = 0;
470       }
471
472       /* auto-respond to PINGs */
473       if((recvflags & CURLWS_PING) && !oleft) {
474         infof(data, "WS: auto-respond to PING with a PONG");
475         /* send back the exact same content as a PONG */
476         result = curl_ws_send(data, out, olen, &bytes, 0, CURLWS_PONG);
477         if(result)
478           return result;
479       }
480       else {
481         if(olen < buflen) {
482           /* copy the payload to the user buffer */
483           memcpy(buffer, out, olen);
484           *nread = olen;
485           if(!oleft)
486             /*  websocket frame has been delivered */
487             ws_decode_clear(data);
488         }
489         else {
490           /* copy a partial payload */
491           memcpy(buffer, out, buflen);
492           *nread = buflen;
493           /* remember what is left and where */
494           wsp->stillbuffer = olen - buflen;
495           wsp->stillb = (char *)buffer + buflen;
496         }
497         wsp->frame.offset += *nread;
498       }
499     }
500     else
501       *nread = bytes;
502     break;
503   } while(1);
504   *metap = &wsp->frame;
505   return CURLE_OK;
506 }
507
508 static void ws_xor(struct Curl_easy *data,
509                    const unsigned char *source,
510                    unsigned char *dest,
511                    size_t len)
512 {
513   struct websocket *wsp = &data->req.p.http->ws;
514   size_t i;
515   /* append payload after the mask, XOR appropriately */
516   for(i = 0; i < len; i++) {
517     dest[i] = source[i] ^ wsp->mask[wsp->xori];
518     wsp->xori++;
519     wsp->xori &= 3;
520   }
521 }
522
523 /***
524     RFC 6455 Section 5.2
525
526       0                   1                   2                   3
527       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
528      +-+-+-+-+-------+-+-------------+-------------------------------+
529      |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
530      |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
531      |N|V|V|V|       |S|             |   (if payload len==126/127)   |
532      | |1|2|3|       |K|             |                               |
533      +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
534      |     Extended payload length continued, if payload len == 127  |
535      + - - - - - - - - - - - - - - - +-------------------------------+
536      |                               |Masking-key, if MASK set to 1  |
537      +-------------------------------+-------------------------------+
538      | Masking-key (continued)       |          Payload Data         |
539      +-------------------------------- - - - - - - - - - - - - - - - +
540      :                     Payload Data continued ...                :
541      + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
542      |                     Payload Data continued ...                |
543      +---------------------------------------------------------------+
544 */
545
546 static size_t ws_packethead(struct Curl_easy *data,
547                             size_t len, unsigned int flags)
548 {
549   struct HTTP *ws = data->req.p.http;
550   unsigned char *out = (unsigned char *)data->state.ulbuf;
551   unsigned char firstbyte = 0;
552   int outi;
553   unsigned char opcode;
554   if(flags & CURLWS_TEXT) {
555     opcode = WSBIT_OPCODE_TEXT;
556     infof(data, "WS: send OPCODE TEXT");
557   }
558   else if(flags & CURLWS_CLOSE) {
559     opcode = WSBIT_OPCODE_CLOSE;
560     infof(data, "WS: send OPCODE CLOSE");
561   }
562   else if(flags & CURLWS_PING) {
563     opcode = WSBIT_OPCODE_PING;
564     infof(data, "WS: send OPCODE PING");
565   }
566   else if(flags & CURLWS_PONG) {
567     opcode = WSBIT_OPCODE_PONG;
568     infof(data, "WS: send OPCODE PONG");
569   }
570   else {
571     opcode = WSBIT_OPCODE_BIN;
572     infof(data, "WS: send OPCODE BINARY");
573   }
574
575   if(!(flags & CURLWS_CONT)) {
576     /* if not marked as continuing, assume this is the final fragment */
577     firstbyte |= WSBIT_FIN | opcode;
578     ws->ws.contfragment = FALSE;
579   }
580   else if(ws->ws.contfragment) {
581     /* the previous fragment was not a final one and this isn't either, keep a
582        CONT opcode and no FIN bit */
583     firstbyte |= WSBIT_OPCODE_CONT;
584   }
585   else {
586     ws->ws.contfragment = TRUE;
587   }
588   out[0] = firstbyte;
589   if(len > 65535) {
590     out[1] = 127 | WSBIT_MASK;
591     out[2] = (len >> 8) & 0xff;
592     out[3] = len & 0xff;
593     outi = 10;
594   }
595   else if(len > 126) {
596     out[1] = 126 | WSBIT_MASK;
597     out[2] = (len >> 8) & 0xff;
598     out[3] = len & 0xff;
599     outi = 4;
600   }
601   else {
602     out[1] = (unsigned char)len | WSBIT_MASK;
603     outi = 2;
604   }
605
606   infof(data, "WS: send FIN bit %u (byte %02x)",
607         firstbyte & WSBIT_FIN ? 1 : 0,
608         firstbyte);
609   infof(data, "WS: send payload len %u", (int)len);
610
611   /* 4 bytes mask */
612   memcpy(&out[outi], &ws->ws.mask, 4);
613
614   if(data->set.upload_buffer_size < (len + 10))
615     return 0;
616
617   /* pass over the mask */
618   outi += 4;
619
620   ws->ws.xori = 0;
621   /* return packet size */
622   return outi;
623 }
624
625 CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
626                                   size_t buflen, size_t *sent,
627                                   curl_off_t totalsize,
628                                   unsigned int sendflags)
629 {
630   CURLcode result;
631   size_t headlen;
632   char *out;
633   ssize_t written;
634   struct websocket *wsp = &data->req.p.http->ws;
635
636   if(!data->set.ws_raw_mode) {
637     result = Curl_get_upload_buffer(data);
638     if(result)
639       return result;
640   }
641   else {
642     if(totalsize || sendflags)
643       return CURLE_BAD_FUNCTION_ARGUMENT;
644   }
645
646   if(data->set.ws_raw_mode) {
647     if(!buflen)
648       /* nothing to do */
649       return CURLE_OK;
650     /* raw mode sends exactly what was requested, and this is from within
651        the write callback */
652     if(Curl_is_in_callback(data))
653       result = Curl_write(data, data->conn->writesockfd, buffer, buflen,
654                           &written);
655     else
656       result = Curl_senddata(data, buffer, buflen, &written);
657
658     infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
659           buflen, written);
660     *sent = written;
661     return result;
662   }
663
664   if(buflen > (data->set.upload_buffer_size - 10))
665     /* don't do more than this in one go */
666     buflen = data->set.upload_buffer_size - 10;
667
668   if(sendflags & CURLWS_OFFSET) {
669     if(totalsize) {
670       /* a frame series 'totalsize' bytes big, this is the first */
671       headlen = ws_packethead(data, totalsize, sendflags);
672       wsp->sleft = totalsize - buflen;
673     }
674     else {
675       headlen = 0;
676       if((curl_off_t)buflen > wsp->sleft) {
677         infof(data, "WS: unaligned frame size (sending %zu instead of %zu)",
678               buflen, wsp->sleft);
679         wsp->sleft = 0;
680       }
681       else
682         wsp->sleft -= buflen;
683     }
684   }
685   else
686     headlen = ws_packethead(data, buflen, sendflags);
687
688   /* headlen is the size of the frame header */
689   out = data->state.ulbuf;
690   if(buflen)
691     /* for PING and PONG etc there might not be a payload */
692     ws_xor(data, buffer, (unsigned char *)out + headlen, buflen);
693
694   if(data->set.connect_only)
695     result = Curl_senddata(data, out, buflen + headlen, &written);
696   else
697     result = Curl_write(data, data->conn->writesockfd, out,
698                         buflen + headlen, &written);
699
700   infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
701         headlen + buflen, written);
702   *sent = written;
703
704   return result;
705 }
706
707 void Curl_ws_done(struct Curl_easy *data)
708 {
709   struct websocket *wsp = &data->req.p.http->ws;
710   DEBUGASSERT(wsp);
711   Curl_dyn_free(&wsp->buf);
712 }
713
714 CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
715 {
716   /* we only return something for websocket, called from within the callback
717      when not using raw mode */
718   if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http &&
719      !data->set.ws_raw_mode)
720     return &data->req.p.http->ws.frame;
721   return NULL;
722 }
723
724 #else
725
726 CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
727                                   size_t *nread,
728                                   struct curl_ws_frame **metap)
729 {
730   (void)curl;
731   (void)buffer;
732   (void)buflen;
733   (void)nread;
734   (void)metap;
735   return CURLE_OK;
736 }
737
738 CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
739                                   size_t buflen, size_t *sent,
740                                   curl_off_t framesize,
741                                   unsigned int sendflags)
742 {
743   (void)curl;
744   (void)buffer;
745   (void)buflen;
746   (void)sent;
747   (void)framesize;
748   (void)sendflags;
749   return CURLE_OK;
750 }
751
752 CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
753 {
754   (void)data;
755   return NULL;
756 }
757 #endif /* USE_WEBSOCKETS */