/src/mosquitto/src/handle_unsubscribe.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 <stdio.h> |
22 | | #include <string.h> |
23 | | |
24 | | #include "mosquitto_broker_internal.h" |
25 | | #include "mosquitto/mqtt_protocol.h" |
26 | | #include "packet_mosq.h" |
27 | | #include "send_mosq.h" |
28 | | |
29 | | |
30 | | int handle__unsubscribe(struct mosquitto *context) |
31 | 1.67k | { |
32 | 1.67k | uint16_t mid; |
33 | 1.67k | uint16_t slen; |
34 | 1.67k | int rc; |
35 | 1.67k | uint8_t reason = 0; |
36 | 1.67k | int reason_code_count = 0; |
37 | 1.67k | int reason_code_max; |
38 | 1.67k | uint8_t *reason_codes = NULL, *reason_tmp; |
39 | 1.67k | mosquitto_property *properties = NULL; |
40 | 1.67k | bool allowed; |
41 | 1.67k | struct mosquitto_subscription sub; |
42 | | |
43 | 1.67k | if(!context){ |
44 | 0 | return MOSQ_ERR_INVAL; |
45 | 0 | } |
46 | | |
47 | 1.67k | if(context->state != mosq_cs_active){ |
48 | 21 | return MOSQ_ERR_PROTOCOL; |
49 | 21 | } |
50 | 1.65k | if(context->in_packet.command != (CMD_UNSUBSCRIBE|2)){ |
51 | 15 | return MOSQ_ERR_MALFORMED_PACKET; |
52 | 15 | } |
53 | 1.63k | log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBSCRIBE from %s", context->id); |
54 | | |
55 | 1.63k | if(context->protocol != mosq_p_mqtt31){ |
56 | 1.56k | if((context->in_packet.command&0x0F) != 0x02){ |
57 | 0 | return MOSQ_ERR_MALFORMED_PACKET; |
58 | 0 | } |
59 | 1.56k | } |
60 | 1.63k | if(packet__read_uint16(&context->in_packet, &mid)){ |
61 | 11 | return MOSQ_ERR_MALFORMED_PACKET; |
62 | 11 | } |
63 | 1.62k | if(mid == 0){ |
64 | 1 | return MOSQ_ERR_MALFORMED_PACKET; |
65 | 1 | } |
66 | | |
67 | 1.62k | if(context->protocol == mosq_p_mqtt5){ |
68 | 1.03k | rc = property__read_all(CMD_UNSUBSCRIBE, &context->in_packet, &properties); |
69 | 1.03k | if(rc){ |
70 | | /* FIXME - it would be better if property__read_all() returned |
71 | | * MOSQ_ERR_MALFORMED_PACKET, but this is would change the library |
72 | | * return codes so needs doc changes as well. */ |
73 | 850 | if(rc == MOSQ_ERR_PROTOCOL){ |
74 | 170 | return MOSQ_ERR_MALFORMED_PACKET; |
75 | 680 | }else{ |
76 | 680 | return rc; |
77 | 680 | } |
78 | 850 | } |
79 | | /* Immediately free, we don't do anything with User Property at the moment */ |
80 | 186 | mosquitto_property_free_all(&properties); |
81 | 186 | } |
82 | | |
83 | 777 | if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ |
84 | 217 | if(context->in_packet.pos == context->in_packet.remaining_length){ |
85 | | /* No topic specified, protocol error. */ |
86 | 9 | return MOSQ_ERR_MALFORMED_PACKET; |
87 | 9 | } |
88 | 217 | } |
89 | | |
90 | 768 | reason_code_max = 10; |
91 | 768 | reason_codes = mosquitto_malloc((size_t)reason_code_max); |
92 | 768 | if(!reason_codes){ |
93 | 0 | return MOSQ_ERR_NOMEM; |
94 | 0 | } |
95 | | |
96 | 5.47M | while(context->in_packet.pos < context->in_packet.remaining_length){ |
97 | 5.47M | memset(&sub, 0, sizeof(sub)); |
98 | 5.47M | sub.properties = properties; |
99 | 5.47M | if(packet__read_string(&context->in_packet, &sub.topic_filter, &slen)){ |
100 | 219 | mosquitto_FREE(reason_codes); |
101 | 219 | return MOSQ_ERR_MALFORMED_PACKET; |
102 | 219 | } |
103 | | |
104 | 5.47M | if(!slen){ |
105 | 5 | log__printf(NULL, MOSQ_LOG_INFO, |
106 | 5 | "Empty unsubscription string from %s, disconnecting.", |
107 | 5 | context->id); |
108 | 5 | mosquitto_FREE(sub.topic_filter); |
109 | 5 | mosquitto_FREE(reason_codes); |
110 | 5 | return MOSQ_ERR_MALFORMED_PACKET; |
111 | 5 | } |
112 | 5.47M | if(mosquitto_sub_topic_check(sub.topic_filter)){ |
113 | 75 | log__printf(NULL, MOSQ_LOG_INFO, |
114 | 75 | "Invalid unsubscription string from %s, disconnecting.", |
115 | 75 | context->id); |
116 | 75 | mosquitto_FREE(sub.topic_filter); |
117 | 75 | mosquitto_FREE(reason_codes); |
118 | 75 | return MOSQ_ERR_MALFORMED_PACKET; |
119 | 75 | } |
120 | | |
121 | | /* ACL check */ |
122 | 5.47M | allowed = true; |
123 | 5.47M | rc = mosquitto_acl_check(context, sub.topic_filter, 0, NULL, 0, false, MOSQ_ACL_UNSUBSCRIBE); |
124 | 5.47M | switch(rc){ |
125 | 0 | case MOSQ_ERR_SUCCESS: |
126 | 0 | break; |
127 | 5.47M | case MOSQ_ERR_ACL_DENIED: |
128 | 5.47M | allowed = false; |
129 | 5.47M | reason = MQTT_RC_NOT_AUTHORIZED; |
130 | 5.47M | break; |
131 | 0 | default: |
132 | 0 | mosquitto_FREE(sub.topic_filter); |
133 | 0 | mosquitto_FREE(reason_codes); |
134 | 0 | return rc; |
135 | 5.47M | } |
136 | | |
137 | 5.47M | log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub.topic_filter); |
138 | 5.47M | if(allowed){ |
139 | 0 | rc = plugin__handle_unsubscribe(context, &sub); |
140 | 0 | if(rc){ |
141 | 0 | mosquitto_FREE(sub.topic_filter); |
142 | 0 | mosquitto_FREE(reason_codes); |
143 | 0 | return rc; |
144 | 0 | } |
145 | 0 | rc = sub__remove(context, sub.topic_filter, &reason); |
146 | 0 | plugin_persist__handle_subscription_delete(context, sub.topic_filter); |
147 | 5.47M | }else{ |
148 | 5.47M | rc = MOSQ_ERR_SUCCESS; |
149 | 5.47M | } |
150 | 5.47M | log__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub.topic_filter); |
151 | 5.47M | mosquitto_FREE(sub.topic_filter); |
152 | 5.47M | if(rc){ |
153 | 0 | mosquitto_FREE(reason_codes); |
154 | 0 | return rc; |
155 | 0 | } |
156 | | |
157 | 5.47M | reason_codes[reason_code_count] = reason; |
158 | 5.47M | reason_code_count++; |
159 | 5.47M | if(reason_code_count == reason_code_max){ |
160 | 1.44k | reason_tmp = mosquitto_realloc(reason_codes, (size_t)(reason_code_max*2)); |
161 | 1.44k | if(!reason_tmp){ |
162 | 0 | mosquitto_FREE(reason_codes); |
163 | 0 | return MOSQ_ERR_NOMEM; |
164 | 0 | } |
165 | 1.44k | reason_codes = reason_tmp; |
166 | 1.44k | reason_code_max *= 2; |
167 | 1.44k | } |
168 | 5.47M | } |
169 | 469 | #ifdef WITH_PERSISTENCE |
170 | 469 | db.persistence_changes++; |
171 | 469 | #endif |
172 | | |
173 | 469 | log__printf(NULL, MOSQ_LOG_DEBUG, "Sending UNSUBACK to %s", context->id); |
174 | | |
175 | | /* We don't use Reason String or User Property yet. */ |
176 | 469 | rc = send__unsuback(context, mid, reason_code_count, reason_codes, NULL); |
177 | | mosquitto_FREE(reason_codes); |
178 | 469 | return rc; |
179 | 768 | } |