ws.c 23 KB

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