Coverage Report

Created: 2025-11-07 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}