✓ProveIt
Examples / Report #3330839 Saved analysis

CVE-2025-10148: predictable WebSocket mask

curl · 2025-09-0812.2s analysis · $0.022 in model calls

What the report claims

The curl WebSocket implementation reuses a fixed masking key for all frames, violating RFC 6455's requirement for unpredictable per-frame masking.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2025-10148 predictable WebSocket mask

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Judgment withdrawn. The answer failed the evidence check.
See the withdrawn model reasoning

The following reasoning failed the citation check and does not support the final finding.

  • The code generates a single 32-bit mask at connection establishment (Curl_ws_accept line 1340) and reuses it for every frame's header and payload masking (ws_enc_write_head line 879, ws_enc_write_payload line 930).
  • RFC 6455 §5.3 explicitly requires a fresh, unpredictable masking key for each frame; the current implementation violates this MUST requirement.
  • The mask is transmitted in clear in every frame header, making it trivial for any observer to learn and predict all subsequent masking keys.
  • This enables the cache poisoning attacks described in RFC 6455 §10.3, where an attacker can craft WebSocket frames that mimic HTTP requests to poison intermediary caches.
  • The flaw exists in both the current master and the latest release (curl-8_15_0), as shown by the provided code for both versions.
How this analysis ran 6 claims · 9 citations verified

Evidence check: judge quoted an external source that does not say that (https://www.ietf.org/rfc/rfc6455.txt)

Checked against curl permalink 455afa1de518 (455afa1de518). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 9 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
6 claims · 2069 tokens · $0.0003
3 · Evidence
5 code snippets
real curl source at 455afa1de518 · 5 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
16530 tokens · 9.2s · $0.0216
5 · Receipts
9 of 9 citations verified
every citation must point at code the judge was shown
Inspect the withdrawn draft reply

This draft belongs to the withdrawn judgment. It may contain unsupported claims.

Thank you for this clear and well-documented report. The code confirms that curl generates one masking key at WebSocket connection establishment and reuses it for all frames, violating RFC 6455 §5.3's requirement for a fresh, unpredictable key per frame. Since the mask is sent in clear in every frame header, an attacker can trivially predict all subsequent masking keys, enabling the cache poisoning attacks described in RFC 6455 §10.3. This is a valid security issue affecting all versions with WebSocket support. We will work on a fix that generates a new random mask for each outgoing frame.

Inspect all claims and source code 6 claims · 5 code excerpts

This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.

Report claims

C1flaw supported

generates a fixed masking key at the beginning of a connection and re-uses it for every frame

Read the reasoning

The mask is generated once at connection acceptance (line 1340) and then reused unchanged in both ws_enc_write_head (line 879) and ws_enc_write_payload (line 930) for all frames sent on that connection.

C2location supported

masking key generation occurs in Curl_ws_accept at lib/ws.c line 1340

Read the reasoning

Line 1340-1341 in master (line 1028-1029 in curl-8_15_0) shows the masking key generation in Curl_ws_accept.

C3location supported

masking key usage occurs in ws_enc_write_head at lib/ws.c line 879

Read the reasoning

Line 879 in master shows the mask being copied into each outgoing frame's header.

C4location supported

masking key usage occurs in ws_enc_write_payload at lib/ws.c line 930

Read the reasoning

Line 930 in master shows the same mask being used to XOR each payload byte.

C5impact supported

trivial for an attacker to guess the masking key of all subsequent frames

Read the reasoning

Since the 32-bit mask is sent in clear in every frame header (line 879) and reused for all payload masking (line 930), an observer who sees one frame learns the mask for all subsequent frames.

C6reproduction supported

None

Read the reasoning

The report accurately states no reproduction steps were provided.

What the report says about other projects

confirmed RFC 6455 §5.3 requires a fresh masking key for each frame: 'When preparing a masked frame, the client MUST pick a fresh masking key from the set of allowed 32-bit values. The masking key needs to be unpredictable; thus, the masking key MUST be derived from a strong source of entropy, and the masking key for a given frame MUST NOT make it simple for a server/proxy to predict the masking key for a subsequent frame.'

“The masking key is a 32-bit value chosen at random by the client. When preparing a masked frame, the client MUST pick a fresh masking key from the set of allowed 32-bit values. The masking key needs to be unpredictable; thus, the masking key MUST be derived from a strong source of entropy, and the masking key for a given frame MUST NOT make it simple for a server/proxy to predict the masking key for a subsequent frame.” source

Read the original report (excerpt)

Full report on HackerOne ↗

No AI was involved. ## Summary: The curl WebSocket implementation generates a fixed masking key at the beginning of a connection an re-uses it for every frame: * Generation of masking key `enc.mask` in `Curl_ws_accept`: https://github.com/curl/curl/blob/455afa1de5182b95a5dcc988f18cdff584b95239/lib/ws.c#L1340 * Usage in `ws_enc_write_head`: https://github.com/curl/curl/blob/455afa1de5182b95a5dcc988f18cdff584b95239/lib/ws.c#L879 * Usage in `ws_enc_write_payload`: https://github.com/curl/curl/blob/455afa1de5182b95a5dcc988f18cdff584b95239/lib/ws.c#L930 [RFC-6455 §5.3](https://datatracker.ietf.org/doc/html/rfc6455#section-5.3) states: > [...] The masking key is a 32-bit value chosen at random by the client. When preparing a masked frame, the client MUST pick a fresh masking key from the set of allowed 32-bit values. The masking key needs to be unpredictable; thus, the masking key MUST be derived from a strong source of entropy, and the masking key for a given frame MUST NOT make it simple for a server/proxy to predict the masking key for a subsequent frame. The unpredictability of the masking key is essential to prevent authors of malicious applications from selecting the bytes that appear on the wire. [...] Because the same masking key is repeatedly used for every frame, it is trivial for an attacker to "guess" the masking key of all subsequent frames. ## Affected version All versions since WebSocket support was added. ## Steps To Reproduce: None ## Supporting Material/References: None ## Impact See "Security Considerations > Attacks On Infrastructure (Masking)" in [RFC-6455 §10.3](https://datatracker.ietf.org/doc/html/rfc6455#section-10.3). Here a short excerpt: > [...] As this protocol was being developed, an experiment was conducted to demonstrate a class of attacks on proxies that led to the poisoning of caching proxies deployed in the wild [TALKING]. The general form of the attack was to establish a connection to a server under the "attacker's" control, perform an UPGRADE on the HTTP connection similar to what the WebSocket Protocol does to establish a connection, and subsequently send data over that UPGRADEd connection that looked like a GET request for a specific known resource (which in an attack would likely be something like a widely deployed script for tracking hits or a resource on an ad-serving network). The remote server would resp …

Source code

lib/ws.clines 1276–1423 · lines 1340-1340 cited by the report; definition of Curl_ws_accept
1276CURLcode Curl_ws_accept(struct Curl_easy *data,
1277 const char *mem, size_t nread)
1278{
1279 struct SingleRequest *k = &data->req;
1280 struct websocket *ws;
1281 struct Curl_cwriter *ws_dec_writer = NULL;
1282 struct Curl_creader *ws_enc_reader = NULL;
1283 CURLcode result;
1284
1285 DEBUGASSERT(data->conn);
1286 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
1287 if(!ws) {
1288 size_t chunk_size = WS_CHUNK_SIZE;
1289 ws = calloc(1, sizeof(*ws));
1290 if(!ws)
1291 return CURLE_OUT_OF_MEMORY;
1292#ifdef DEBUGBUILD
1293 {
1294 const char *p = getenv("CURL_WS_CHUNK_SIZE");
1295 if(p) {
1296 curl_off_t l;
1297 if(!curlx_str_number(&p, &l, 1*1024*1024))
1298 chunk_size = (size_t)l;
1299 }
1300 }
1301#endif
1302 CURL_TRC_WS(data, "WS, using chunk size %zu", chunk_size);
1303 Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT,
1304 BUFQ_OPT_SOFT_LIMIT);
1305 Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT,
1306 BUFQ_OPT_SOFT_LIMIT);
1307 ws_dec_init(&ws->dec);
1308 ws_enc_init(&ws->enc);
1309 result = Curl_conn_meta_set(data->conn, CURL_META_PROTO_WS_CONN,
1310 ws, ws_conn_dtor);
1311 if(result)
1312 return result;
1313 }
1314 else {
1315 Curl_bufq_reset(&ws->recvbuf);
1316 ws_dec_reset(&ws->dec);
1317 ws_enc_reset(&ws->enc);
1318 }
1319 /* Verify the Sec-WebSocket-Accept response.
1320
1321 The sent value is the base64 encoded version of a SHA-1 hash done on the
1322 |Sec-WebSocket-Key| header field concatenated with
1323 the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
1324 */
1325
1326 /* If the response includes a |Sec-WebSocket-Extensions| header field and
1327 this header field indicates the use of an extension that was not present
1328 in the client's handshake (the server has indicated an extension not
1329 requested by the client), the client MUST Fail the WebSocket Connection.
1330 */
1331
1332 /* If the response includes a |Sec-WebSocket-Protocol| header field
1333 and this header field indicates the use of a subprotocol that was
1334 not present in the client's handshake (the server has indicated a
1335 subprotocol not requested by the client), the client MUST Fail
1336 the WebSocket Connection. */
1337
1338 /* 4 bytes random */
1339
1340 result = Curl_rand(data, (unsigned char *)&ws->enc.mask,
C1: Curl_rand generates mask once in Curl_ws_accept
C2: Curl_rand call generating ws->enc.mask
C5: Single 32-bit mask generated once
1341 sizeof(ws->enc.mask));
1342 if(result)
1343 return result;
1344
1345#ifdef DEBUGBUILD
1346 if(getenv("CURL_WS_FORCE_ZERO_MASK"))
1347 /* force the bit mask to 0x00000000, effectively disabling masking */
1348 memset(ws->enc.mask, 0, sizeof(ws->enc.mask));
1349#endif
1350
1351 infof(data, "[WS] Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
1352 ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]);
1353
1354 /* Install our client writer that decodes WS frames payload */
1355 result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode,
1356 CURL_CW_CONTENT_DECODE);
1357 if(result)
1358 goto out;
1359 result = Curl_cwriter_add(data, ws_dec_writer);
1360 if(result)
1361 goto out;
1362 ws_dec_writer = NULL; /* owned by transfer now */
1363
1364 if(data->set.connect_only) {
1365 size_t nwritten;
1366 /* In CONNECT_ONLY setup, the payloads from `mem` need to be received
1367 * when using `curl_ws_recv` later on after this transfer is already
1368 * marked as DONE. */
1369 result = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem,
1370 nread, &nwritten);
1371 if(result)
1372 goto out;
1373 DEBUGASSERT(nread == nwritten);
1374 k->keepon &= ~KEEP_RECV; /* read no more content */
1375 }
1376 else { /* !connect_only */
1377 if(data->set.method == HTTPREQ_PUT) {
1378 CURL_TRC_WS(data, "UPLOAD set, add ws-encode reader");
1379 result = Curl_creader_set_fread(data, -1);
1380 if(result)
1381 goto out;
1382
1383 if(!data->set.ws_raw_mode) {
1384 /* Add our client readerr encoding WS BINARY frames */
1385 result = Curl_creader_create(&ws_enc_reader, data, &ws_cr_encode,
1386 CURL_CR_CONTENT_ENCODE);
1387 if(result)
1388 goto out;
1389 result = Curl_creader_add(data, ws_enc_reader);
1390 if(result)
1391 goto out;
1392 ws_enc_reader = NULL; /* owned by transfer now */
1393 }
1394
1395 /* start over with sending */
1396 data->req.eos_read = FALSE;
1397 data->req.upload_done = FALSE;
1398 k->keepon |= KEEP_SEND;
1399 }
1400
1401 /* And pass any additional data to the writers */
1402 if(nread) {
1403 result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread);
1404 if(result)
1405 goto out;
1406 }
1407 }
1408
1409 k->upgr101 = UPGR101_RECEIVED;
1410 k->header = FALSE; /* we will not get more responses */
1411
1412out:
1413 if(ws_dec_writer)
1414 Curl_cwriter_free(data, ws_dec_writer);
1415 if(ws_enc_reader)
1416 Curl_creader_free(data, ws_enc_reader);
1417 if(result)
1418 CURL_TRC_WS(data, "Curl_ws_accept() failed -> %d", result);
1419 else
1420 CURL_TRC_WS(data, "websocket established, %s mode",
1421 data->set.connect_only ? "connect-only" : "callback");
1422 return result;
1423}
lib/ws.clines 854–955 · lines 879-879 cited by the report; lines 930-930 cited by the report
854 head[2] = (unsigned char)((payload_len >> 56) & 0xff);
855 head[3] = (unsigned char)((payload_len >> 48) & 0xff);
856 head[4] = (unsigned char)((payload_len >> 40) & 0xff);
857 head[5] = (unsigned char)((payload_len >> 32) & 0xff);
858 head[6] = (unsigned char)((payload_len >> 24) & 0xff);
859 head[7] = (unsigned char)((payload_len >> 16) & 0xff);
860 head[8] = (unsigned char)((payload_len >> 8) & 0xff);
861 head[9] = (unsigned char)(payload_len & 0xff);
862 hlen = 10;
863 }
864 else if(payload_len >= 126) {
865 head[1] = 126 | WSBIT_MASK;
866 head[2] = (unsigned char)((payload_len >> 8) & 0xff);
867 head[3] = (unsigned char)(payload_len & 0xff);
868 hlen = 4;
869 }
870 else {
871 head[1] = (unsigned char)payload_len | WSBIT_MASK;
872 hlen = 2;
873 }
874
875 enc->payload_remain = enc->payload_len = payload_len;
876 ws_enc_info(enc, data, "sending");
877
878 /* add 4 bytes mask */
879 memcpy(&head[hlen], &enc->mask, 4);
C1: ws_enc_write_head copies enc->mask into every frame header
C3: memcpy(&head[hlen], &enc->mask, 4) adds mask to frame header
C5: Same mask sent in every frame header (unmasked)
880 hlen += 4;
881 /* reset for payload to come */
882 enc->xori = 0;
883
884 result = Curl_bufq_write(out, head, hlen, &nwritten);
885 if(result)
886 return result;
887 if(nwritten != hlen) {
888 /* We use a bufq with SOFT_LIMIT, writing should always succeed */
889 DEBUGASSERT(0);
890 return CURLE_SEND_ERROR;
891 }
892 return CURLE_OK;
893}
894
895static CURLcode ws_enc_write_head(struct Curl_easy *data,
896 struct websocket *ws,
897 struct ws_encoder *enc,
898 unsigned int flags,
899 curl_off_t payload_len,
900 struct bufq *out)
901{
902 /* starting a new frame, we want a clean sendbuf.
903 * Any pending control frame we can add now as part of the flush. */
904 if(ws->pending.type) {
905 CURLcode result = ws_enc_add_pending(data, ws);
906 if(result)
907 return result;
908 }
909 return ws_enc_add_frame(data, enc, flags, payload_len, out);
910}
911
912static CURLcode ws_enc_write_payload(struct ws_encoder *enc,
913 struct Curl_easy *data,
914 const unsigned char *buf, size_t buflen,
915 struct bufq *out, size_t *pnwritten)
916{
917 CURLcode result;
918 size_t i, len, n;
919
920 *pnwritten = 0;
921 if(Curl_bufq_is_full(out))
922 return CURLE_AGAIN;
923
924 /* not the most performant way to do this */
925 len = buflen;
926 if((curl_off_t)len > enc->payload_remain)
927 len = (size_t)enc->payload_remain;
928
929 for(i = 0; i < len; ++i) {
930 unsigned char c = buf[i] ^ enc->mask[enc->xori];
C1: ws_enc_write_payload XORs payload with same enc->mask for every frame
C4: buf[i] ^ enc->mask[enc->xori] uses mask for payload masking
C5: Same mask used for all payload XOR operations
931 result = Curl_bufq_write(out, &c, 1, &n);
932 if(result) {
933 if((result != CURLE_AGAIN) || !i)
934 return result;
935 break;
936 }
937 enc->xori++;
938 enc->xori &= 3;
939 }
940 *pnwritten = i;
941 enc->payload_remain -= (curl_off_t)i;
942 ws_enc_info(enc, data, "buffered");
943 return CURLE_OK;
944}
945
946static CURLcode ws_enc_add_pending(struct Curl_easy *data,
947 struct websocket *ws)
948{
949 CURLcode result;
950 size_t n;
951
952 if(!ws->pending.type) /* no pending frame here */
953 return CURLE_OK;
954 if(ws->enc.payload_remain) /* in the middle of another frame */
955 return CURLE_AGAIN;
lib/ws.c@curl-8_15_0lines 965–1075 · definition of Curl_ws_accept as in release curl-8_15_0; differs from master; commits that changed it since: 5fa4fb0c13 websocket: reset upload_done when sending data | 37cecfc7b9 websocket: support CURLOPT_READFUNCTION
Show 111 lines (not cited by the judge)
965CURLcode Curl_ws_accept(struct Curl_easy *data,
966 const char *mem, size_t nread)
967{
968 struct SingleRequest *k = &data->req;
969 struct websocket *ws;
970 struct Curl_cwriter *ws_dec_writer;
971 CURLcode result;
972
973 DEBUGASSERT(data->conn);
974 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
975 if(!ws) {
976 size_t chunk_size = WS_CHUNK_SIZE;
977 ws = calloc(1, sizeof(*ws));
978 if(!ws)
979 return CURLE_OUT_OF_MEMORY;
980#ifdef DEBUGBUILD
981 {
982 const char *p = getenv("CURL_WS_CHUNK_SIZE");
983 if(p) {
984 curl_off_t l;
985 if(!curlx_str_number(&p, &l, 1*1024*1024))
986 chunk_size = (size_t)l;
987 }
988 }
989#endif
990 CURL_TRC_WS(data, "WS, using chunk size %zu", chunk_size);
991 Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT,
992 BUFQ_OPT_SOFT_LIMIT);
993 Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT,
994 BUFQ_OPT_SOFT_LIMIT);
995 ws_dec_init(&ws->dec);
996 ws_enc_init(&ws->enc);
997 result = Curl_conn_meta_set(data->conn, CURL_META_PROTO_WS_CONN,
998 ws, ws_conn_dtor);
999 if(result)
1000 return result;
1001 }
1002 else {
1003 Curl_bufq_reset(&ws->recvbuf);
1004 ws_dec_reset(&ws->dec);
1005 ws_enc_reset(&ws->enc);
1006 }
1007 /* Verify the Sec-WebSocket-Accept response.
1008
1009 The sent value is the base64 encoded version of a SHA-1 hash done on the
1010 |Sec-WebSocket-Key| header field concatenated with
1011 the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
1012 */
1013
1014 /* If the response includes a |Sec-WebSocket-Extensions| header field and
1015 this header field indicates the use of an extension that was not present
1016 in the client's handshake (the server has indicated an extension not
1017 requested by the client), the client MUST Fail the WebSocket Connection.
1018 */
1019
1020 /* If the response includes a |Sec-WebSocket-Protocol| header field
1021 and this header field indicates the use of a subprotocol that was
1022 not present in the client's handshake (the server has indicated a
1023 subprotocol not requested by the client), the client MUST Fail
1024 the WebSocket Connection. */
1025
1026 /* 4 bytes random */
1027
1028 result = Curl_rand(data, (unsigned char *)&ws->enc.mask,
1029 sizeof(ws->enc.mask));
1030 if(result)
1031 return result;
1032
1033#ifdef DEBUGBUILD
1034 if(getenv("CURL_WS_FORCE_ZERO_MASK"))
1035 /* force the bit mask to 0x00000000, effectively disabling masking */
1036 memset(ws->enc.mask, 0, sizeof(ws->enc.mask));
1037#endif
1038
1039 infof(data, "[WS] Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
1040 ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]);
1041
1042 /* Install our client writer that decodes WS frames payload */
1043 result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode,
1044 CURL_CW_CONTENT_DECODE);
1045 if(result)
1046 return result;
1047
1048 result = Curl_cwriter_add(data, ws_dec_writer);
1049 if(result) {
1050 Curl_cwriter_free(data, ws_dec_writer);
1051 return result;
1052 }
1053
1054 if(data->set.connect_only) {
1055 size_t nwritten;
1056 /* In CONNECT_ONLY setup, the payloads from `mem` need to be received
1057 * when using `curl_ws_recv` later on after this transfer is already
1058 * marked as DONE. */
1059 result = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem,
1060 nread, &nwritten);
1061 if(result)
1062 return result;
1063 DEBUGASSERT(nread == nwritten);
1064 infof(data, "%zu bytes websocket payload", nread);
1065 }
1066 else { /* !connect_only */
1067 /* And pass any additional data to the writers */
1068 if(nread) {
1069 result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread);
1070 }
1071 }
1072 k->upgr101 = UPGR101_RECEIVED;
1073
1074 return result;
1075}
lib/ws.c@curl-8_15_0lines 750–849 · definition of ws_enc_write_head as in release curl-8_15_0; differs from master; commits that changed it since: 0b09132877 websocket: handling of PONG frames
Show 100 lines (not cited by the judge)
750static ssize_t ws_enc_write_head(struct Curl_easy *data,
751 struct ws_encoder *enc,
752 unsigned int flags,
753 curl_off_t payload_len,
754 struct bufq *out,
755 CURLcode *err)
756{
757 unsigned char firstbyte = 0;
758 unsigned char head[14];
759 size_t hlen, n;
760
761 if(payload_len < 0) {
762 failf(data, "[WS] starting new frame with negative payload length %"
763 FMT_OFF_T, payload_len);
764 *err = CURLE_SEND_ERROR;
765 return -1;
766 }
767
768 if(enc->payload_remain > 0) {
769 /* trying to write a new frame before the previous one is finished */
770 failf(data, "[WS] starting new frame with %zd bytes from last one "
771 "remaining to be sent", (ssize_t)enc->payload_remain);
772 *err = CURLE_SEND_ERROR;
773 return -1;
774 }
775
776 firstbyte = ws_frame_flags2firstbyte(data, flags, enc->contfragment, err);
777 if(*err) {
778 return -1;
779 }
780
781 /* fragmentation only applies to data frames (text/binary);
782 * control frames (close/ping/pong) do not affect the CONT status */
783 if(flags & (CURLWS_TEXT | CURLWS_BINARY)) {
784 enc->contfragment = (flags & CURLWS_CONT) ? (bit)TRUE : (bit)FALSE;
785 }
786
787 if(flags & CURLWS_PING && payload_len > 125) {
788 /* The maximum valid size of PING frames is 125 bytes. */
789 failf(data, "[WS] given PING frame is too big");
790 *err = CURLE_TOO_LARGE;
791 return -1;
792 }
793 if(flags & CURLWS_PONG && payload_len > 125) {
794 /* The maximum valid size of PONG frames is 125 bytes. */
795 failf(data, "[WS] given PONG frame is too big");
796 *err = CURLE_TOO_LARGE;
797 return -1;
798 }
799 if(flags & CURLWS_CLOSE && payload_len > 125) {
800 /* The maximum valid size of CLOSE frames is 125 bytes. */
801 failf(data, "[WS] given CLOSE frame is too big");
802 *err = CURLE_TOO_LARGE;
803 return -1;
804 }
805
806 head[0] = enc->firstbyte = firstbyte;
807 if(payload_len > 65535) {
808 head[1] = 127 | WSBIT_MASK;
809 head[2] = (unsigned char)((payload_len >> 56) & 0xff);
810 head[3] = (unsigned char)((payload_len >> 48) & 0xff);
811 head[4] = (unsigned char)((payload_len >> 40) & 0xff);
812 head[5] = (unsigned char)((payload_len >> 32) & 0xff);
813 head[6] = (unsigned char)((payload_len >> 24) & 0xff);
814 head[7] = (unsigned char)((payload_len >> 16) & 0xff);
815 head[8] = (unsigned char)((payload_len >> 8) & 0xff);
816 head[9] = (unsigned char)(payload_len & 0xff);
817 hlen = 10;
818 }
819 else if(payload_len >= 126) {
820 head[1] = 126 | WSBIT_MASK;
821 head[2] = (unsigned char)((payload_len >> 8) & 0xff);
822 head[3] = (unsigned char)(payload_len & 0xff);
823 hlen = 4;
824 }
825 else {
826 head[1] = (unsigned char)payload_len | WSBIT_MASK;
827 hlen = 2;
828 }
829
830 enc->payload_remain = enc->payload_len = payload_len;
831 ws_enc_info(enc, data, "sending");
832
833 /* add 4 bytes mask */
834 memcpy(&head[hlen], &enc->mask, 4);
835 hlen += 4;
836 /* reset for payload to come */
837 enc->xori = 0;
838
839 *err = Curl_bufq_write(out, head, hlen, &n);
840 if(*err)
841 return -1;
842 if(n != hlen) {
843 /* We use a bufq with SOFT_LIMIT, writing should always succeed */
844 DEBUGASSERT(0);
845 *err = CURLE_SEND_ERROR;
846 return -1;
847 }
848 return (ssize_t)n;
849}
lib/ws.c@curl-8_15_0lines 851–882 · definition of ws_enc_write_payload as in release curl-8_15_0; differs from master; commits that changed it since: 37cecfc7b9 websocket: support CURLOPT_READFUNCTION
Show 32 lines (not cited by the judge)
851static ssize_t ws_enc_write_payload(struct ws_encoder *enc,
852 struct Curl_easy *data,
853 const unsigned char *buf, size_t buflen,
854 struct bufq *out, CURLcode *err)
855{
856 size_t i, len, n;
857
858 if(Curl_bufq_is_full(out)) {
859 *err = CURLE_AGAIN;
860 return -1;
861 }
862
863 /* not the most performant way to do this */
864 len = buflen;
865 if((curl_off_t)len > enc->payload_remain)
866 len = (size_t)enc->payload_remain;
867
868 for(i = 0; i < len; ++i) {
869 unsigned char c = buf[i] ^ enc->mask[enc->xori];
870 *err = Curl_bufq_write(out, &c, 1, &n);
871 if(*err) {
872 if((*err != CURLE_AGAIN) || !i)
873 return -1;
874 break;
875 }
876 enc->xori++;
877 enc->xori &= 3;
878 }
879 enc->payload_remain -= (curl_off_t)i;
880 ws_enc_info(enc, data, "buffered");
881 return (ssize_t)i;
882}