Coverage Report

Created: 2025-11-14 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mosquitto/lib/net_ws.c
Line
Count
Source
1
/*
2
Copyright (c) 2021 Roger Light <roger@atchoo.org>
3
4
All rights reserved. This program and the accompanying materials
5
are made available under the terms of the Eclipse Public License 2.0
6
and Eclipse Distribution License v1.0 which accompany this distribution.
7
8
The Eclipse Public License is available at
9
   https://www.eclipse.org/legal/epl-2.0/
10
and the Eclipse Distribution License is available at
11
  http://www.eclipse.org/org/documents/edl-v10.php.
12
13
SPDX-License-Identifier: EPL-2.0 OR EDL-1.0
14
15
Contributors:
16
   Roger Light - initial implementation and documentation.
17
*/
18
19
#include "config.h"
20
21
#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN
22
#ifndef WITH_TLS
23
#  error "Builtin websockets support requires WITH_TLS=yes and openssl to be available"
24
#endif
25
26
#include <errno.h>
27
#include <stddef.h>
28
#include <string.h>
29
30
#include "mosquitto_internal.h"
31
#include "mosquitto/mqtt_protocol.h"
32
#include "net_mosq.h"
33
#include "packet_mosq.h"
34
#include "util_mosq.h"
35
36
37
void ws__context_init(struct mosquitto *mosq)
38
0
{
39
0
  mosq->transport = mosq_t_ws;
40
0
  mosq->state = mosq_cs_new;
41
0
}
42
43
44
void ws__prepare_packet(struct mosquitto *mosq, struct mosquitto__packet *packet)
45
0
{
46
0
  uint8_t opcode;
47
0
  uint32_t masking_offset = mosq->wsd.is_client?4:0;
48
49
0
  packet->next = NULL;
50
51
0
  if(mosq->wsd.opcode == UINT8_MAX){
52
0
    opcode = WS_BINARY;
53
0
  }else if(mosq->wsd.opcode == WS_PING){
54
0
    opcode = WS_PONG;
55
0
  }else{
56
0
    opcode = mosq->wsd.opcode;
57
0
  }
58
0
  if(packet->packet_length - WS_PACKET_OFFSET < 126){
59
0
    if(mosq->wsd.is_client){
60
0
      packet->payload[WS_PACKET_OFFSET-masking_offset-1] = (uint8_t)(packet->packet_length-WS_PACKET_OFFSET) | 0x80;
61
0
    }else{
62
0
      packet->payload[WS_PACKET_OFFSET-masking_offset-1] = (uint8_t)(packet->packet_length-WS_PACKET_OFFSET);
63
0
    }
64
0
    packet->payload[WS_PACKET_OFFSET - masking_offset-2] = 0x80 | opcode;
65
0
    packet->pos = WS_PACKET_OFFSET - masking_offset - 2;
66
0
    packet->to_process = packet->packet_length - WS_PACKET_OFFSET + masking_offset + 2;
67
0
  }else if(packet->packet_length - WS_PACKET_OFFSET < 65536){
68
0
    uint16_t plen = (uint16_t )(packet->packet_length - WS_PACKET_OFFSET);
69
70
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-1] = MOSQ_LSB(plen);
71
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-2] = MOSQ_MSB(plen);;
72
0
    if(mosq->wsd.is_client){
73
0
      packet->payload[WS_PACKET_OFFSET-masking_offset-3] = 126 | 0x80;
74
0
    }else{
75
0
      packet->payload[WS_PACKET_OFFSET-masking_offset-3] = 126;
76
0
    }
77
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-4] = 0x80 | opcode;
78
0
    packet->pos = WS_PACKET_OFFSET-masking_offset - 4;
79
0
    packet->to_process = packet->packet_length - WS_PACKET_OFFSET + masking_offset + 4;
80
0
  }else{
81
0
    uint64_t plen = packet->packet_length - WS_PACKET_OFFSET;
82
83
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-1] = (uint8_t)((plen & 0x00000000000000FF) >> 0);
84
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-2] = (uint8_t)((plen & 0x000000000000FF00) >> 8);
85
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-3] = (uint8_t)((plen & 0x0000000000FF0000) >> WS_PACKET_OFFSET);
86
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-4] = (uint8_t)((plen & 0x00000000FF000000) >> 24);
87
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-5] = (uint8_t)((plen & 0x000000FF00000000) >> 32);
88
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-6] = (uint8_t)((plen & 0x0000FF0000000000) >> 40);
89
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-7] = (uint8_t)((plen & 0x00FF000000000000) >> 48);
90
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-8] = (uint8_t)((plen & 0xFF00000000000000) >> 56);
91
0
    if(mosq->wsd.is_client){
92
0
      packet->payload[WS_PACKET_OFFSET-masking_offset-9] = 127 | 0x80;
93
0
    }else{
94
0
      packet->payload[WS_PACKET_OFFSET-masking_offset-9] = 127;
95
0
    }
96
0
    packet->payload[WS_PACKET_OFFSET-masking_offset-10] = 0x80 | opcode;
97
0
    packet->pos = WS_PACKET_OFFSET-masking_offset - 10;
98
0
    packet->to_process = packet->packet_length - WS_PACKET_OFFSET + masking_offset + 10;
99
0
  }
100
0
  if(mosq->wsd.is_client){
101
0
    mosquitto_getrandom(&packet->payload[WS_PACKET_OFFSET-sizeof(uint32_t)], sizeof(uint32_t));
102
0
    for(uint32_t i=0; i<packet->packet_length - WS_PACKET_OFFSET; i++){
103
0
      packet->payload[WS_PACKET_OFFSET + i] ^= packet->payload[WS_PACKET_OFFSET-sizeof(uint32_t)+(i%4)];
104
0
    }
105
0
  }
106
0
}
107
108
109
static ssize_t read_ws_opcode(struct mosquitto *mosq)
110
0
{
111
0
  ssize_t len;
112
0
  uint8_t opcode;
113
0
  uint8_t fin;
114
0
  uint8_t hbuf;
115
116
0
  mosq->wsd.mask_bytes = 4;
117
0
  mosq->wsd.pos = 0;
118
0
  mosq->wsd.mask = UINT8_MAX;
119
0
  mosq->wsd.payloadlen_bytes = UINT8_MAX;
120
121
0
  len = net__read(mosq, &hbuf, 1);
122
0
  if(len <= 0){
123
0
    return len;
124
0
  }
125
126
0
  if((hbuf & 0x70) != 0x00){
127
0
    mosq->wsd.disconnect_reason = 0xEA;
128
0
    errno = EPROTO;
129
0
    return -1;
130
0
  }
131
0
  opcode = (hbuf & 0x0F);
132
0
  fin = (hbuf & 0x80);
133
0
  switch(opcode){
134
0
    case WS_CONTINUATION:
135
0
    case WS_BINARY:
136
0
    case WS_PING:
137
0
    case WS_PONG:
138
0
    case WS_CLOSE:
139
0
      mosq->wsd.opcode = opcode;
140
0
      break;
141
142
0
    case WS_TEXT:
143
0
      mosq->wsd.disconnect_reason = 0xEB;
144
0
      errno = EPROTO;
145
0
      return -1;
146
147
0
    default:
148
0
      mosq->wsd.disconnect_reason = 0xEA;
149
0
      errno = EPROTO;
150
0
      return -1;
151
0
      break;
152
0
  }
153
0
  if((opcode & 0x08) && fin == 0){
154
    /* Control packets may not be fragmented */
155
0
    mosq->wsd.disconnect_reason = 0xEA;
156
0
    errno = EPROTO;
157
0
    return -1;
158
0
  }
159
160
0
  return len;
161
0
}
162
163
164
static ssize_t read_ws_payloadlen_short(struct mosquitto *mosq)
165
0
{
166
0
  ssize_t len;
167
0
  uint8_t hbuf;
168
0
  uint8_t plen;
169
170
0
  len = net__read(mosq, &hbuf, 1);
171
0
  if(len <= 0){
172
0
    return len;
173
0
  }
174
175
0
  mosq->wsd.mask = (hbuf & 0x80) >> 7;
176
0
  plen = hbuf & 0x7F;
177
178
0
  if(plen == 126){
179
0
    mosq->wsd.payloadlen_bytes = 2;
180
0
    mosq->wsd.payloadlen = 0;
181
0
  }else if(plen == 127){
182
0
    mosq->wsd.payloadlen_bytes = 8;
183
0
    mosq->wsd.payloadlen = 0;
184
0
  }else{
185
0
    mosq->wsd.payloadlen_bytes = 0;
186
0
    mosq->wsd.payloadlen = plen;
187
0
  }
188
189
0
  return len;
190
0
}
191
192
193
static ssize_t read_ws_payloadlen_extended(struct mosquitto *mosq)
194
0
{
195
0
  uint8_t hbuf[8];
196
0
  ssize_t len;
197
198
0
  len = net__read(mosq, hbuf, mosq->wsd.payloadlen_bytes);
199
0
  if(len <= 0){
200
0
    return len;
201
0
  }
202
0
  for(ssize_t i=0; i<len; i++){
203
0
    mosq->wsd.payloadlen = (mosq->wsd.payloadlen << 8) + hbuf[i];
204
0
  }
205
0
  mosq->wsd.payloadlen_bytes -= (uint8_t)len;
206
207
0
  return len;
208
0
}
209
210
211
static ssize_t read_ws_mask(struct mosquitto *mosq)
212
0
{
213
0
  ssize_t len;
214
215
0
  len = net__read(mosq, &mosq->wsd.maskingkey[4-mosq->wsd.mask_bytes], mosq->wsd.mask_bytes);
216
0
  if(len <= 0){
217
0
    return len;
218
0
  }
219
0
  mosq->wsd.mask_bytes -= (uint8_t)len;
220
0
  if(mosq->wsd.mask_bytes > 0){
221
0
    errno = EAGAIN;
222
0
    return -1;
223
0
  }
224
225
0
  return len;
226
0
}
227
228
229
ssize_t net__read_ws(struct mosquitto *mosq, void *buf, size_t count)
230
0
{
231
0
  ssize_t len = 0;
232
233
0
  if(mosq->wsd.payloadlen == 0){
234
0
    if(mosq->wsd.opcode == UINT8_MAX){
235
0
      len = read_ws_opcode(mosq);
236
0
      if(len <= 0){
237
0
        return len;
238
0
      }
239
0
    }
240
241
0
    if(mosq->wsd.mask == UINT8_MAX){
242
0
      len = read_ws_payloadlen_short(mosq);
243
0
      if(len <= 0){
244
0
        return len;
245
0
      }
246
0
    }
247
248
0
    if(mosq->wsd.payloadlen_bytes > 0){
249
0
      len = read_ws_payloadlen_extended(mosq);
250
0
      if(len <= 0){
251
0
        return len;
252
0
      }
253
0
    }
254
255
0
    if(mosq->wsd.mask == 1 && mosq->wsd.mask_bytes > 0){
256
0
      len = read_ws_mask(mosq);
257
0
      if(len <= 0){
258
0
        return len;
259
0
      }
260
0
    }
261
262
0
    if(mosq->wsd.opcode == WS_CLOSE && mosq->wsd.payloadlen == 1){
263
0
      mosq->wsd.disconnect_reason = 0xEA;
264
0
      errno = EPROTO;
265
0
      return -1;
266
0
    }else if(mosq->wsd.payloadlen > 125 && mosq->wsd.opcode != WS_BINARY && mosq->wsd.opcode != WS_CONTINUATION){
267
0
      mosq->wsd.disconnect_reason = 0xEA;
268
0
      errno = EPROTO;
269
0
      return -1;
270
0
    }
271
272
0
    if(mosq->wsd.payloadlen > MQTT_MAX_PAYLOAD){
273
0
      errno = EPROTO;
274
0
      return -1;
275
0
    }
276
277
0
#ifndef WS_TESTING
278
0
    if(mosq->wsd.opcode == WS_PING || (mosq->wsd.opcode == WS_CLOSE && mosq->wsd.payloadlen >= 2))
279
    /* Always allocate payload for testing case, otherwise just for pings */
280
0
#endif
281
0
    {
282
0
      mosq->wsd.out_packet = mosquitto_calloc(1, sizeof(struct mosquitto__packet) + WS_PACKET_OFFSET + mosq->wsd.payloadlen + 1);
283
0
      if(mosq->wsd.out_packet == NULL){
284
0
        errno = ENOMEM;
285
0
        return -1;
286
0
      }
287
0
      mosq->wsd.out_packet->packet_length = (uint32_t)mosq->wsd.payloadlen + WS_PACKET_OFFSET;
288
0
    }
289
0
  }
290
291
0
  if(mosq->wsd.out_packet){
292
    /* This means we are either dealing with protocol level messages (and
293
     * hence won't be returning MQTT data to the context), or we are
294
     * testing and should be echoing data back to the client.
295
     * So ignore what data is being asked for, and try and read the whole
296
     * lot at once. */
297
0
    count = mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos;
298
0
    buf = &mosq->wsd.out_packet->payload[WS_PACKET_OFFSET + mosq->wsd.pos];
299
0
  }
300
301
0
  if(mosq->wsd.payloadlen > 0){
302
0
    if(count > mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos){
303
0
      count = mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos;
304
0
    }
305
0
    len = net__read(mosq, buf, count);
306
0
    if(len > 0){
307
0
      for(ssize_t i=0; i<len; i++){
308
0
        ((uint8_t *)buf)[i] ^= mosq->wsd.maskingkey[(i+mosq->wsd.pos)%4];
309
0
      }
310
0
      mosq->wsd.pos += len;
311
0
    }
312
0
  }
313
314
0
  if(mosq->wsd.pos == (ssize_t)mosq->wsd.payloadlen){
315
0
    if(mosq->wsd.opcode == WS_CLOSE){
316
0
      mosquitto_FREE(mosq->wsd.out_packet);
317
318
      /* Testing or PING - so we haven't read any data for the application yet. */
319
0
      len = -1;
320
0
      errno = EAGAIN;
321
0
    }else if(mosq->wsd.opcode == WS_PONG){
322
0
      mosquitto_FREE(mosq->wsd.out_packet);
323
      /* Testing or PING - so we haven't read any data for the application yet. */
324
0
      len = -1;
325
0
      errno = EAGAIN;
326
0
    }else if(mosq->wsd.out_packet){
327
0
      packet__queue(mosq, mosq->wsd.out_packet);
328
0
      mosq->wsd.out_packet = NULL;
329
330
      /* Testing or PING - so we haven't read any data for the application yet.
331
      * Simulate that situation. This has to be done *after* the call to
332
      * packet__queue. */
333
0
      len = -1;
334
0
      errno = EAGAIN;
335
0
    }
336
0
    mosq->wsd.payloadlen = 0;
337
0
    mosq->wsd.opcode = UINT8_MAX;
338
0
    mosq->wsd.mask = UINT8_MAX;
339
0
  }else if(mosq->wsd.out_packet){
340
    /* Testing or PING - so we haven't read any data for the application yet.
341
    * Simulate that situation.*/
342
0
    len = -1;
343
0
    errno = EAGAIN;
344
0
  }
345
0
  return len;
346
0
}
347
348
349
int ws__create_accept_key(const char *client_key, size_t client_key_len, char **encoded)
350
0
{
351
0
  const EVP_MD *digest;
352
0
  EVP_MD_CTX *evp = NULL;
353
0
  uint8_t accept_key_hash[EVP_MAX_MD_SIZE];
354
0
  unsigned int accept_key_hash_len;
355
356
0
  digest = EVP_get_digestbyname("sha1");
357
0
  if(!digest){
358
0
    return MOSQ_ERR_UNKNOWN;
359
0
  }
360
361
0
  evp = EVP_MD_CTX_new();
362
0
  if(evp && EVP_DigestInit_ex(evp, digest, NULL) != 0){
363
0
    if(EVP_DigestUpdate(evp, client_key, client_key_len) != 0){
364
0
      if(EVP_DigestUpdate(evp, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
365
0
          strlen("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")) != 0){
366
367
0
        if(EVP_DigestFinal_ex(evp, accept_key_hash, &accept_key_hash_len) != 0){
368
0
          EVP_MD_CTX_free(evp);
369
0
          return mosquitto_base64_encode(accept_key_hash, accept_key_hash_len, encoded);
370
0
        }
371
0
      }
372
0
    }
373
0
  }
374
0
  EVP_MD_CTX_free(evp);
375
0
  return MOSQ_ERR_UNKNOWN;
376
0
}
377
378
379
#endif