Coverage Report

Created: 2025-08-26 06:48

/src/mosquitto/lib/net_ws.c
Line
Count
Source (jump to first uncovered line)
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) return len;
123
124
0
  if((hbuf & 0x70) != 0x00){
125
0
    mosq->wsd.disconnect_reason = 0xEA;
126
0
    errno = EPROTO;
127
0
    return -1;
128
0
  }
129
0
  opcode = (hbuf & 0x0F);
130
0
  fin = (hbuf & 0x80);
131
0
  switch(opcode){
132
0
    case WS_CONTINUATION:
133
0
    case WS_BINARY:
134
0
    case WS_PING:
135
0
    case WS_PONG:
136
0
    case WS_CLOSE:
137
0
      mosq->wsd.opcode = opcode;
138
0
      break;
139
140
0
    case WS_TEXT:
141
0
      mosq->wsd.disconnect_reason = 0xEB;
142
0
      errno = EPROTO;
143
0
      return -1;
144
145
0
    default:
146
0
      mosq->wsd.disconnect_reason = 0xEA;
147
0
      errno = EPROTO;
148
0
      return -1;
149
0
      break;
150
0
  }
151
0
  if((opcode & 0x08) && fin == 0){
152
    /* Control packets may not be fragmented */
153
0
    mosq->wsd.disconnect_reason = 0xEA;
154
0
    errno = EPROTO;
155
0
    return -1;
156
0
  }
157
158
0
  return len;
159
0
}
160
161
162
static ssize_t read_ws_payloadlen_short(struct mosquitto *mosq)
163
0
{
164
0
  ssize_t len;
165
0
  uint8_t hbuf;
166
0
  uint8_t plen;
167
168
0
  len = net__read(mosq, &hbuf, 1);
169
0
  if(len <= 0) return len;
170
171
0
  mosq->wsd.mask = (hbuf & 0x80) >> 7;
172
0
  plen = hbuf & 0x7F;
173
174
0
  if(plen == 126){
175
0
    mosq->wsd.payloadlen_bytes = 2;
176
0
    mosq->wsd.payloadlen = 0;
177
0
  }else if(plen == 127){
178
0
    mosq->wsd.payloadlen_bytes = 8;
179
0
    mosq->wsd.payloadlen = 0;
180
0
  }else{
181
0
    mosq->wsd.payloadlen_bytes = 0;
182
0
    mosq->wsd.payloadlen = plen;
183
0
  }
184
185
0
  return len;
186
0
}
187
188
189
static ssize_t read_ws_payloadlen_extended(struct mosquitto *mosq)
190
0
{
191
0
  uint8_t hbuf[8];
192
0
  ssize_t len;
193
194
0
  len = net__read(mosq, hbuf, mosq->wsd.payloadlen_bytes);
195
0
  if(len <= 0) return len;
196
0
  for(ssize_t i=0; i<len; i++){
197
0
    mosq->wsd.payloadlen = (mosq->wsd.payloadlen << 8) + hbuf[i];
198
0
  }
199
0
  mosq->wsd.payloadlen_bytes -= (uint8_t)len;
200
201
0
  return len;
202
0
}
203
204
205
static ssize_t read_ws_mask(struct mosquitto *mosq)
206
0
{
207
0
  ssize_t len;
208
209
0
  len = net__read(mosq, &mosq->wsd.maskingkey[4-mosq->wsd.mask_bytes], mosq->wsd.mask_bytes);
210
0
  if(len <= 0) return len;
211
0
  mosq->wsd.mask_bytes -= (uint8_t)len;
212
0
  if(mosq->wsd.mask_bytes > 0){
213
0
    errno = EAGAIN;
214
0
    return -1;
215
0
  }
216
217
0
  return len;
218
0
}
219
220
221
ssize_t net__read_ws(struct mosquitto *mosq, void *buf, size_t count)
222
0
{
223
0
  ssize_t len = 0;
224
225
0
  if(mosq->wsd.payloadlen == 0){
226
0
    if(mosq->wsd.opcode == UINT8_MAX){
227
0
      len = read_ws_opcode(mosq);
228
0
      if(len <= 0) return len;
229
0
    }
230
231
0
    if(mosq->wsd.mask == UINT8_MAX){
232
0
      len = read_ws_payloadlen_short(mosq);
233
0
      if(len <= 0) return len;
234
0
    }
235
236
0
    if(mosq->wsd.payloadlen_bytes > 0){
237
0
      len = read_ws_payloadlen_extended(mosq);
238
0
      if(len <= 0) return len;
239
0
    }
240
241
0
    if(mosq->wsd.mask == 1 && mosq->wsd.mask_bytes > 0){
242
0
      len = read_ws_mask(mosq);
243
0
      if(len <= 0) return len;
244
0
    }
245
246
0
    if(mosq->wsd.opcode == WS_CLOSE && mosq->wsd.payloadlen == 1){
247
0
      mosq->wsd.disconnect_reason = 0xEA;
248
0
      errno = EPROTO;
249
0
      return -1;
250
0
    }else if(mosq->wsd.payloadlen > 125 && mosq->wsd.opcode != WS_BINARY && mosq->wsd.opcode != WS_CONTINUATION){
251
0
      mosq->wsd.disconnect_reason = 0xEA;
252
0
      errno = EPROTO;
253
0
      return -1;
254
0
    }
255
256
0
    if(mosq->wsd.payloadlen > MQTT_MAX_PAYLOAD){
257
0
      errno = EPROTO;
258
0
      return -1;
259
0
    }
260
261
0
#ifndef WS_TESTING
262
0
    if(mosq->wsd.opcode == WS_PING || (mosq->wsd.opcode == WS_CLOSE && mosq->wsd.payloadlen >= 2))
263
    /* Always allocate payload for testing case, otherwise just for pings */
264
0
#endif
265
0
    {
266
0
      mosq->wsd.out_packet = mosquitto_calloc(1, sizeof(struct mosquitto__packet) + WS_PACKET_OFFSET + mosq->wsd.payloadlen + 1);
267
0
      if(mosq->wsd.out_packet == NULL){
268
0
        errno = ENOMEM;
269
0
        return -1;
270
0
      }
271
0
      mosq->wsd.out_packet->packet_length = (uint32_t)mosq->wsd.payloadlen + WS_PACKET_OFFSET;
272
0
    }
273
0
  }
274
275
0
  if(mosq->wsd.out_packet){
276
    /* This means we are either dealing with protocol level messages (and
277
     * hence won't be returning MQTT data to the context), or we are
278
     * testing and should be echoing data back to the client.
279
     * So ignore what data is being asked for, and try and read the whole
280
     * lot at once. */
281
0
    count = mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos;
282
0
    buf = &mosq->wsd.out_packet->payload[WS_PACKET_OFFSET + mosq->wsd.pos];
283
0
  }
284
285
0
  if(mosq->wsd.payloadlen > 0){
286
0
    if(count > mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos){
287
0
      count = mosq->wsd.payloadlen - (uint64_t)mosq->wsd.pos;
288
0
    }
289
0
    len = net__read(mosq, buf, count);
290
0
    if(len > 0){
291
0
      for(ssize_t i=0; i<len; i++){
292
0
        ((uint8_t *)buf)[i] ^= mosq->wsd.maskingkey[(i+mosq->wsd.pos)%4];
293
0
      }
294
0
      mosq->wsd.pos += len;
295
0
    }
296
0
  }
297
298
0
  if(mosq->wsd.pos == (ssize_t)mosq->wsd.payloadlen){
299
0
    if(mosq->wsd.opcode == WS_CLOSE){
300
0
      mosquitto_FREE(mosq->wsd.out_packet);
301
302
      /* Testing or PING - so we haven't read any data for the application yet. */
303
0
      len = -1;
304
0
      errno = EAGAIN;
305
0
    }else if(mosq->wsd.opcode == WS_PONG){
306
0
      mosquitto_FREE(mosq->wsd.out_packet);
307
      /* Testing or PING - so we haven't read any data for the application yet. */
308
0
      len = -1;
309
0
      errno = EAGAIN;
310
0
    }else if(mosq->wsd.out_packet){
311
0
      packet__queue(mosq, mosq->wsd.out_packet);
312
0
      mosq->wsd.out_packet = NULL;
313
314
      /* Testing or PING - so we haven't read any data for the application yet.
315
      * Simulate that situation. This has to be done *after* the call to
316
      * packet__queue. */
317
0
      len = -1;
318
0
      errno = EAGAIN;
319
0
    }
320
0
    mosq->wsd.payloadlen = 0;
321
0
    mosq->wsd.opcode = UINT8_MAX;
322
0
    mosq->wsd.mask = UINT8_MAX;
323
0
  }else if(mosq->wsd.out_packet){
324
    /* Testing or PING - so we haven't read any data for the application yet.
325
    * Simulate that situation.*/
326
0
    len = -1;
327
0
    errno = EAGAIN;
328
0
  }
329
0
  return len;
330
0
}
331
332
int ws__create_accept_key(const char *client_key, size_t client_key_len, char **encoded)
333
0
{
334
0
  const EVP_MD *digest;
335
0
  EVP_MD_CTX *evp = NULL;
336
0
  uint8_t accept_key_hash[EVP_MAX_MD_SIZE];
337
0
  unsigned int accept_key_hash_len;
338
339
0
  digest = EVP_get_digestbyname("sha1");
340
0
  if(!digest){
341
0
    return MOSQ_ERR_UNKNOWN;
342
0
  }
343
344
0
  evp = EVP_MD_CTX_new();
345
0
  if(evp && EVP_DigestInit_ex(evp, digest, NULL) != 0){
346
0
    if(EVP_DigestUpdate(evp, client_key, client_key_len) != 0){
347
0
      if(EVP_DigestUpdate(evp, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
348
0
          strlen("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")) != 0){
349
350
0
        if(EVP_DigestFinal_ex(evp, accept_key_hash, &accept_key_hash_len) != 0){
351
0
          EVP_MD_CTX_free(evp);
352
0
          return mosquitto_base64_encode(accept_key_hash, accept_key_hash_len, encoded);
353
0
        }
354
0
      }
355
0
    }
356
0
  }
357
0
  EVP_MD_CTX_free(evp);
358
0
  return MOSQ_ERR_UNKNOWN;
359
0
}
360
361
362
#endif