1 /*
2 Copyright (c) 2016-2020 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 "mosquitto_broker_internal.h"
22 #include "mosquitto_internal.h"
23 #include "mosquitto_broker.h"
24 #include "memory_mosq.h"
25 #include "mqtt_protocol.h"
26 #include "send_mosq.h"
27 #include "util_mosq.h"
28 #include "utlist.h"
29
30 #ifdef WITH_TLS
31 # include <openssl/ssl.h>
32 #endif
33
mosquitto_client_address(const struct mosquitto * client)34 const char *mosquitto_client_address(const struct mosquitto *client)
35 {
36 if(client){
37 return client->address;
38 }else{
39 return NULL;
40 }
41 }
42
43
mosquitto_client_clean_session(const struct mosquitto * client)44 bool mosquitto_client_clean_session(const struct mosquitto *client)
45 {
46 if(client){
47 return client->clean_start;
48 }else{
49 return true;
50 }
51 }
52
53
mosquitto_client_id(const struct mosquitto * client)54 const char *mosquitto_client_id(const struct mosquitto *client)
55 {
56 if(client){
57 return client->id;
58 }else{
59 return NULL;
60 }
61 }
62
63
mosquitto_client_keepalive(const struct mosquitto * client)64 int mosquitto_client_keepalive(const struct mosquitto *client)
65 {
66 if(client){
67 return client->keepalive;
68 }else{
69 return -1;
70 }
71 }
72
73
mosquitto_client_certificate(const struct mosquitto * client)74 void *mosquitto_client_certificate(const struct mosquitto *client)
75 {
76 #ifdef WITH_TLS
77 if(client && client->ssl){
78 return SSL_get_peer_certificate(client->ssl);
79 }else{
80 return NULL;
81 }
82 #else
83 UNUSED(client);
84
85 return NULL;
86 #endif
87 }
88
89
mosquitto_client_protocol(const struct mosquitto * client)90 int mosquitto_client_protocol(const struct mosquitto *client)
91 {
92 #ifdef WITH_WEBSOCKETS
93 if(client && client->wsi){
94 return mp_websockets;
95 }else
96 #else
97 UNUSED(client);
98 #endif
99 {
100 return mp_mqtt;
101 }
102 }
103
104
mosquitto_client_protocol_version(const struct mosquitto * client)105 int mosquitto_client_protocol_version(const struct mosquitto *client)
106 {
107 if(client){
108 switch(client->protocol){
109 case mosq_p_mqtt31:
110 return 3;
111 case mosq_p_mqtt311:
112 return 4;
113 case mosq_p_mqtt5:
114 return 5;
115 default:
116 return 0;
117 }
118 }else{
119 return 0;
120 }
121 }
122
123
mosquitto_client_sub_count(const struct mosquitto * client)124 int mosquitto_client_sub_count(const struct mosquitto *client)
125 {
126 if(client){
127 return client->sub_count;
128 }else{
129 return 0;
130 }
131 }
132
133
mosquitto_client_username(const struct mosquitto * client)134 const char *mosquitto_client_username(const struct mosquitto *client)
135 {
136 if(client){
137 #ifdef WITH_BRIDGE
138 if(client->bridge){
139 return client->bridge->local_username;
140 }else
141 #endif
142 {
143 return client->username;
144 }
145 }else{
146 return NULL;
147 }
148 }
149
150
mosquitto_broker_publish(const char * clientid,const char * topic,int payloadlen,void * payload,int qos,bool retain,mosquitto_property * properties)151 int mosquitto_broker_publish(
152 const char *clientid,
153 const char *topic,
154 int payloadlen,
155 void *payload,
156 int qos,
157 bool retain,
158 mosquitto_property *properties)
159 {
160 struct mosquitto_message_v5 *msg;
161
162 if(topic == NULL
163 || payloadlen < 0
164 || (payloadlen > 0 && payload == NULL)
165 || qos < 0 || qos > 2){
166
167 return MOSQ_ERR_INVAL;
168 }
169
170 msg = mosquitto__malloc(sizeof(struct mosquitto_message_v5));
171 if(msg == NULL) return MOSQ_ERR_NOMEM;
172
173 msg->next = NULL;
174 msg->prev = NULL;
175 if(clientid){
176 msg->clientid = mosquitto__strdup(clientid);
177 if(msg->clientid == NULL){
178 mosquitto__free(msg);
179 return MOSQ_ERR_NOMEM;
180 }
181 }else{
182 msg->clientid = NULL;
183 }
184 msg->topic = mosquitto__strdup(topic);
185 if(msg->topic == NULL){
186 mosquitto__free(msg->clientid);
187 mosquitto__free(msg);
188 return MOSQ_ERR_NOMEM;
189 }
190 msg->payloadlen = payloadlen;
191 msg->payload = payload;
192 msg->qos = qos;
193 msg->retain = retain;
194 msg->properties = properties;
195
196 DL_APPEND(db.plugin_msgs, msg);
197
198 return MOSQ_ERR_SUCCESS;
199 }
200
201
mosquitto_broker_publish_copy(const char * clientid,const char * topic,int payloadlen,const void * payload,int qos,bool retain,mosquitto_property * properties)202 int mosquitto_broker_publish_copy(
203 const char *clientid,
204 const char *topic,
205 int payloadlen,
206 const void *payload,
207 int qos,
208 bool retain,
209 mosquitto_property *properties)
210 {
211 void *payload_out;
212
213 if(topic == NULL
214 || payloadlen < 0
215 || (payloadlen > 0 && payload == NULL)
216 || qos < 0 || qos > 2){
217
218 return MOSQ_ERR_INVAL;
219 }
220
221 payload_out = calloc(1, (size_t)(payloadlen+1));
222 if(payload_out == NULL){
223 return MOSQ_ERR_NOMEM;
224 }
225 memcpy(payload_out, payload, (size_t)payloadlen);
226
227 return mosquitto_broker_publish(
228 clientid,
229 topic,
230 payloadlen,
231 payload_out,
232 qos,
233 retain,
234 properties);
235 }
236
237
mosquitto_set_username(struct mosquitto * client,const char * username)238 int mosquitto_set_username(struct mosquitto *client, const char *username)
239 {
240 char *u_dup;
241 char *old;
242 int rc;
243
244 if(!client) return MOSQ_ERR_INVAL;
245
246 if(username){
247 u_dup = mosquitto__strdup(username);
248 if(!u_dup) return MOSQ_ERR_NOMEM;
249 }else{
250 u_dup = NULL;
251 }
252
253 old = client->username;
254 client->username = u_dup;
255
256 rc = acl__find_acls(client);
257 if(rc){
258 client->username = old;
259 mosquitto__free(u_dup);
260 return rc;
261 }else{
262 mosquitto__free(old);
263 return MOSQ_ERR_SUCCESS;
264 }
265 }
266
267
268 /* Check to see whether durable clients still have rights to their subscriptions. */
check_subscription_acls(struct mosquitto * context)269 static void check_subscription_acls(struct mosquitto *context)
270 {
271 int i;
272 int rc;
273 uint8_t reason;
274
275 for(i=0; i<context->sub_count; i++){
276 if(context->subs[i] == NULL){
277 continue;
278 }
279 rc = mosquitto_acl_check(context,
280 context->subs[i]->topic_filter,
281 0,
282 NULL,
283 0, /* FIXME */
284 false,
285 MOSQ_ACL_SUBSCRIBE);
286
287 if(rc != MOSQ_ERR_SUCCESS){
288 sub__remove(context, context->subs[i]->topic_filter, db.subs, &reason);
289 }
290 }
291 }
292
293
294
disconnect_client(struct mosquitto * context,bool with_will)295 static void disconnect_client(struct mosquitto *context, bool with_will)
296 {
297 if(context->protocol == mosq_p_mqtt5){
298 send__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL);
299 }
300 if(with_will == false){
301 mosquitto__set_state(context, mosq_cs_disconnecting);
302 }
303 if(context->session_expiry_interval > 0){
304 check_subscription_acls(context);
305 }
306 do_disconnect(context, MOSQ_ERR_ADMINISTRATIVE_ACTION);
307 }
308
mosquitto_kick_client_by_clientid(const char * clientid,bool with_will)309 int mosquitto_kick_client_by_clientid(const char *clientid, bool with_will)
310 {
311 struct mosquitto *ctxt, *ctxt_tmp;
312
313 if(clientid == NULL){
314 HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){
315 disconnect_client(ctxt, with_will);
316 }
317 return MOSQ_ERR_SUCCESS;
318 }else{
319 HASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), ctxt);
320 if(ctxt){
321 disconnect_client(ctxt, with_will);
322 return MOSQ_ERR_SUCCESS;
323 }else{
324 return MOSQ_ERR_NOT_FOUND;
325 }
326 }
327 }
328
mosquitto_kick_client_by_username(const char * username,bool with_will)329 int mosquitto_kick_client_by_username(const char *username, bool with_will)
330 {
331 struct mosquitto *ctxt, *ctxt_tmp;
332
333 if(username == NULL){
334 HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){
335 if(ctxt->username == NULL){
336 disconnect_client(ctxt, with_will);
337 }
338 }
339 }else{
340 HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){
341 if(ctxt->username != NULL && !strcmp(ctxt->username, username)){
342 disconnect_client(ctxt, with_will);
343 }
344 }
345 }
346 return MOSQ_ERR_SUCCESS;
347 }
348