Coverage Report

Created: 2025-11-24 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mosquitto/src/context.c
Line
Count
Source
1
/*
2
Copyright (c) 2009-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 BSD-3-Clause
14
15
Contributors:
16
   Roger Light - initial implementation and documentation.
17
*/
18
19
#include "config.h"
20
21
#include <time.h>
22
#if defined(__APPLE__) || defined(_AIX)
23
#include <sys/socket.h>
24
#endif
25
26
#include "mosquitto_broker_internal.h"
27
#include "alias_mosq.h"
28
#include "packet_mosq.h"
29
#include "property_mosq.h"
30
#include "sys_tree.h"
31
#include "util_mosq.h"
32
#include "will_mosq.h"
33
34
#include "uthash.h"
35
36
37
int context__init_sock(struct mosquitto *context, mosq_sock_t sock, bool get_address)
38
0
{
39
0
  context->sock = sock;
40
41
0
  if((int)context->sock >= 0){
42
0
    if(get_address){
43
0
      char address[1024];
44
45
0
      if(!net__socket_get_address(context->sock,
46
0
          address, sizeof(address),
47
0
          &context->remote_port)){
48
49
0
        context->address = mosquitto_strdup(address);
50
0
      }
51
0
      if(!context->address){
52
        /* getpeername and inet_ntop failed and not a bridge */
53
0
        return MOSQ_ERR_NOMEM;
54
0
      }
55
0
    }
56
0
    HASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context);
57
0
  }
58
0
  return MOSQ_ERR_SUCCESS;
59
0
}
60
61
struct mosquitto *context__init(void)
62
0
{
63
0
  struct mosquitto *context;
64
65
0
  context = mosquitto_calloc(1, sizeof(struct mosquitto));
66
0
  if(!context){
67
0
    return NULL;
68
0
  }
69
70
0
  context->in_packet.packet_buffer_size = db.config->packet_buffer_size;
71
0
  context->in_packet.packet_buffer = mosquitto_calloc(1, context->in_packet.packet_buffer_size);
72
0
  if(!context->in_packet.packet_buffer){
73
0
    mosquitto_FREE(context);
74
0
    return NULL;
75
0
  }
76
77
0
#if defined(WITH_EPOLL) || defined(WITH_KQUEUE)
78
0
  context->ident = id_client;
79
#else
80
  context->pollfd_index = -1;
81
#endif
82
0
  mosquitto__set_state(context, mosq_cs_new);
83
0
  context->sock = INVALID_SOCKET;
84
0
  context->last_msg_in = db.now_s;
85
0
  context->next_msg_out = db.now_s + 20;
86
0
  context->keepalive = 20; /* Default to 20s */
87
0
  context->clean_start = true;
88
0
  context->id = NULL;
89
0
  context->last_mid = 0;
90
0
  context->will = NULL;
91
0
  context->username = NULL;
92
0
  context->password = NULL;
93
0
  context->listener = NULL;
94
0
  context->acl_list = NULL;
95
0
  context->retain_available = true;
96
0
#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN
97
0
  memset(&context->wsd, 0, sizeof(context->wsd));
98
0
  context->wsd.opcode = UINT8_MAX;
99
0
  context->wsd.mask = UINT8_MAX;
100
0
  context->wsd.disconnect_reason = 0xE8;
101
0
#endif
102
103
  /* is_bridge records whether this client is a bridge or not. This could be
104
   * done by looking at context->bridge for bridges that we create ourself,
105
   * but incoming bridges need some other way of being recorded. */
106
0
  context->is_bridge = false;
107
108
0
  context->in_packet.payload = NULL;
109
0
  packet__cleanup(&context->in_packet);
110
0
  context->out_packet = NULL;
111
0
  context->out_packet_count = 0;
112
0
  context->out_packet_bytes = 0;
113
114
0
  context->address = NULL;
115
0
  context->bridge = NULL;
116
0
  context->msgs_in.inflight_maximum = db.config->max_inflight_messages;
117
0
  context->msgs_in.inflight_quota = db.config->max_inflight_messages;
118
0
  context->msgs_out.inflight_maximum = db.config->max_inflight_messages;
119
0
  context->msgs_out.inflight_quota = db.config->max_inflight_messages;
120
0
  context->max_qos = 2;
121
0
#ifdef WITH_TLS
122
0
  context->ssl = NULL;
123
0
#endif
124
125
0
  return context;
126
0
}
127
128
129
static void context__cleanup_out_packets(struct mosquitto *context)
130
0
{
131
0
  struct mosquitto__packet *packet;
132
133
0
  if(!context){
134
0
    return;
135
0
  }
136
137
0
  while(context->out_packet){
138
0
    packet = context->out_packet;
139
0
    context->out_packet = context->out_packet->next;
140
0
    mosquitto_free(packet);
141
0
  }
142
0
  metrics__int_dec(mosq_gauge_out_packets, context->out_packet_count);
143
0
  metrics__int_dec(mosq_gauge_out_packet_bytes, context->out_packet_bytes);
144
0
  context->out_packet_count = 0;
145
0
  context->out_packet_bytes = 0;
146
0
}
147
148
149
/*
150
 * This will result in any outgoing packets going unsent. If we're disconnected
151
 * forcefully then it is usually an error condition and shouldn't be a problem,
152
 * but it will mean that CONNACK messages will never get sent for bad protocol
153
 * versions for example.
154
 */
155
void context__cleanup(struct mosquitto *context, bool force_free)
156
0
{
157
0
  if(!context){
158
0
    return;
159
0
  }
160
161
0
  if(force_free){
162
0
    context->clean_start = true;
163
0
  }
164
165
0
#ifdef WITH_BRIDGE
166
0
  if(context->bridge){
167
0
    bridge__cleanup(context);
168
0
  }
169
0
#endif
170
0
  mosquitto_FREE(context->in_packet.packet_buffer);
171
0
  context->in_packet.packet_buffer_size = 0;
172
173
0
  alias__free_all(context);
174
0
  keepalive__remove(context);
175
0
  context__cleanup_out_packets(context);
176
177
0
  mosquitto_FREE(context->auth_method);
178
0
  mosquitto_FREE(context->username);
179
0
  mosquitto_FREE(context->password);
180
181
0
  net__socket_close(context);
182
0
  if(force_free){
183
0
    sub__clean_session(context);
184
0
  }
185
0
  db__messages_delete(context, force_free);
186
187
0
  mosquitto_FREE(context->address);
188
189
0
  context__send_will(context);
190
191
0
  if(context->id){
192
0
    context__remove_from_by_id(context);
193
0
    mosquitto_FREE(context->id);
194
0
  }
195
0
  packet__cleanup(&(context->in_packet));
196
0
  context__cleanup_out_packets(context);
197
#if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS)
198
  if(context->adns){
199
    gai_cancel(context->adns);
200
    struct addrinfo *ar_request = (struct addrinfo *)context->adns->ar_request;
201
    mosquitto_FREE(ar_request);
202
    mosquitto_FREE(context->adns);
203
  }
204
#endif
205
206
0
#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN
207
0
  mosquitto_FREE(context->http_request);
208
0
#endif
209
0
  mosquitto_FREE(context->proxy.buf);
210
0
  if(force_free){
211
0
    mosquitto_FREE(context);
212
0
  }
213
0
}
214
215
216
void context__send_will(struct mosquitto *ctxt)
217
0
{
218
0
  if(ctxt->state != mosq_cs_disconnecting && ctxt->will){
219
0
    if(ctxt->will_delay_interval > 0){
220
0
      will_delay__add(ctxt);
221
0
      return;
222
0
    }
223
224
0
    if(mosquitto_acl_check(ctxt,
225
0
        ctxt->will->msg.topic,
226
0
        (uint32_t)ctxt->will->msg.payloadlen,
227
0
        ctxt->will->msg.payload,
228
0
        (uint8_t)ctxt->will->msg.qos,
229
0
        ctxt->will->msg.retain,
230
0
        MOSQ_ACL_WRITE) == MOSQ_ERR_SUCCESS){
231
232
      /* Unexpected disconnect, queue the client will. */
233
0
      db__messages_easy_queue(ctxt,
234
0
          ctxt->will->msg.topic,
235
0
          (uint8_t)ctxt->will->msg.qos,
236
0
          (uint32_t)ctxt->will->msg.payloadlen,
237
0
          ctxt->will->msg.payload,
238
0
          ctxt->will->msg.retain,
239
0
          ctxt->will->expiry_interval,
240
0
          &ctxt->will->properties);
241
0
    }
242
0
  }
243
0
  will__clear(ctxt);
244
0
}
245
246
247
void context__disconnect(struct mosquitto *context, int reason)
248
0
{
249
0
  if(mosquitto__get_state(context) == mosq_cs_disconnected){
250
0
    return;
251
0
  }
252
253
0
#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN
254
0
  if(context->transport == mosq_t_ws){
255
0
    uint8_t buf[4] = {0x88, 0x02, 0x03, context->wsd.disconnect_reason};
256
    /* Send the disconnect reason, but don't care if it fails */
257
0
    if(send(context->sock, buf, 4, 0)){
258
0
    }
259
0
  }
260
0
#endif
261
0
  if(context->id){
262
0
    struct mosquitto *context_found;
263
0
    HASH_FIND(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context_found);
264
0
    if(context_found == context){
265
0
      net__socket_close(context);
266
0
      context__add_to_disused(context);
267
0
      return;
268
0
    }
269
0
  }
270
271
0
  if(context->session_expiry_interval == 0){
272
0
    plugin__handle_disconnect(context, reason);
273
0
  }else{
274
0
    plugin__handle_client_offline(context, reason);
275
0
  }
276
277
0
  context__send_will(context);
278
0
  net__socket_close(context);
279
0
#ifdef WITH_BRIDGE
280
0
  if(context->bridge == NULL)
281
  /* Outgoing bridge connection never expire */
282
0
#endif
283
0
  {
284
0
    if(context->session_expiry_interval == 0){
285
0
      plugin_persist__handle_client_delete(context);
286
      /* Client session is due to be expired now */
287
0
      if(context->will_delay_interval == 0){
288
        /* This will be done later, after the will is published for delay>0. */
289
0
        context__add_to_disused(context);
290
0
      }
291
0
    }else{
292
0
      session_expiry__add(context);
293
0
    }
294
0
  }
295
0
  keepalive__remove(context);
296
0
  mosquitto__set_state(context, mosq_cs_disconnected);
297
0
  alias__free_all(context);
298
0
  context__cleanup_out_packets(context);
299
0
}
300
301
302
void context__add_to_disused(struct mosquitto *context)
303
0
{
304
0
  if(context->state == mosq_cs_disused){
305
0
    return;
306
0
  }
307
308
0
  mosquitto__set_state(context, mosq_cs_disused);
309
310
0
  context__remove_from_by_id(context);
311
312
0
  context->for_free_next = db.ll_for_free;
313
0
  db.ll_for_free = context;
314
0
}
315
316
317
void context__free_disused(void)
318
8.25k
{
319
8.25k
  struct mosquitto *context, *next;
320
#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS
321
  struct mosquitto *last = NULL;
322
#endif
323
324
8.25k
  context = db.ll_for_free;
325
8.25k
  db.ll_for_free = NULL;
326
8.25k
  while(context){
327
#if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_LWS
328
    if(context->wsi){
329
      /* Don't delete yet, lws hasn't finished with it */
330
      if(last){
331
        last->for_free_next = context;
332
      }else{
333
        db.ll_for_free = context;
334
      }
335
      next = context->for_free_next;
336
      context->for_free_next = NULL;
337
      last = context;
338
      context = next;
339
    }else
340
#endif
341
0
    {
342
0
      next = context->for_free_next;
343
0
      context__cleanup(context, true);
344
0
      context = next;
345
0
    }
346
0
  }
347
8.25k
}
348
349
350
void context__add_to_by_id(struct mosquitto *context)
351
0
{
352
0
  if(context->in_by_id == false){
353
0
    context->in_by_id = true;
354
0
    HASH_VALUE(context->id, strlen(context->id), context->id_hashv);
355
0
    HASH_ADD_KEYPTR_BYHASHVALUE(hh_id, db.contexts_by_id, context->id, strlen(context->id), context->id_hashv, context);
356
0
  }
357
0
}
358
359
360
void context__remove_from_by_id(struct mosquitto *context)
361
0
{
362
0
  struct mosquitto *context_found;
363
364
0
  if(!context->id){
365
0
    return;
366
0
  }
367
368
0
  if(context->in_by_id){
369
0
    HASH_FIND(hh_id, db.contexts_by_id, context->id, strlen(context->id), context_found);
370
0
    if(context_found == context){
371
0
      HASH_DELETE(hh_id, db.contexts_by_id, context_found);
372
0
    }
373
0
    context->id_hashv = 0;
374
0
    context->in_by_id = false;
375
0
    return;
376
0
  }
377
378
0
  HASH_FIND(hh_id, db.contexts_by_id_delayed_auth, context->id, strlen(context->id), context_found);
379
0
  if(context_found == context){
380
    HASH_DELETE(hh_id, db.contexts_by_id_delayed_auth, context_found);
381
0
  }
382
0
}