/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 | } |