iotivity 0.9.0
[platform/upstream/iotivity.git] / service / protocol-plugin / plugins / mqtt-light / lib / util_mosq.c
1 /*
2 Copyright (c) 2009-2013 Roger Light <roger@atchoo.org>
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9    this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. Neither the name of mosquitto nor the names of its
14    contributors may be used to endorse or promote products derived from
15    this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <assert.h>
31 #include <string.h>
32
33 #ifdef WIN32
34 #include <winsock2.h>
35 #endif
36
37
38 #include "mosquitto.h"
39 #include "memory_mosq.h"
40 #include "net_mosq.h"
41 #include "send_mosq.h"
42 #include "time_mosq.h"
43 #include "tls_mosq.h"
44 #include "util_mosq.h"
45
46 #ifdef WITH_BROKER
47 #include "mosquitto_broker.h"
48 #endif
49
50 int _mosquitto_packet_alloc(struct _mosquitto_packet *packet)
51 {
52         uint8_t remaining_bytes[5], byte;
53         uint32_t remaining_length;
54         int i;
55
56         assert(packet);
57
58         remaining_length = packet->remaining_length;
59         packet->payload = NULL;
60         packet->remaining_count = 0;
61         do{
62                 byte = remaining_length % 128;
63                 remaining_length = remaining_length / 128;
64                 /* If there are more digits to encode, set the top bit of this digit */
65                 if(remaining_length > 0){
66                         byte = byte | 0x80;
67                 }
68                 remaining_bytes[packet->remaining_count] = byte;
69                 packet->remaining_count++;
70         }while(remaining_length > 0 && packet->remaining_count < 5);
71         if(packet->remaining_count == 5) return MOSQ_ERR_PAYLOAD_SIZE;
72         packet->packet_length = packet->remaining_length + 1 + packet->remaining_count;
73         packet->payload = _mosquitto_malloc(sizeof(uint8_t)*packet->packet_length);
74         if(!packet->payload) return MOSQ_ERR_NOMEM;
75
76         packet->payload[0] = packet->command;
77         for(i=0; i<packet->remaining_count; i++){
78                 packet->payload[i+1] = remaining_bytes[i];
79         }
80         packet->pos = 1 + packet->remaining_count;
81
82         return MOSQ_ERR_SUCCESS;
83 }
84
85 void _mosquitto_check_keepalive(struct mosquitto *mosq)
86 {
87         time_t last_msg_out;
88         time_t last_msg_in;
89         time_t now = mosquitto_time();
90 #ifndef WITH_BROKER
91         int rc;
92 #endif
93
94         assert(mosq);
95 #if defined(WITH_BROKER) && defined(WITH_BRIDGE)
96         /* Check if a lazy bridge should be timed out due to idle. */
97         if(mosq->bridge && mosq->bridge->start_type == bst_lazy
98                                 && mosq->sock != INVALID_SOCKET
99                                 && now - mosq->last_msg_out >= mosq->bridge->idle_timeout){
100
101                 _mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id);
102                 _mosquitto_socket_close(mosq);
103                 return;
104         }
105 #endif
106         pthread_mutex_lock(&mosq->msgtime_mutex);
107         last_msg_out = mosq->last_msg_out;
108         last_msg_in = mosq->last_msg_in;
109         pthread_mutex_unlock(&mosq->msgtime_mutex);
110         if(mosq->keepalive && mosq->sock != INVALID_SOCKET &&
111                         (now - last_msg_out >= mosq->keepalive || now - last_msg_in >= mosq->keepalive)){
112
113                 if(mosq->state == mosq_cs_connected && mosq->ping_t == 0){
114                         _mosquitto_send_pingreq(mosq);
115                         /* Reset last msg times to give the server time to send a pingresp */
116                         pthread_mutex_lock(&mosq->msgtime_mutex);
117                         mosq->last_msg_in = now;
118                         mosq->last_msg_out = now;
119                         pthread_mutex_unlock(&mosq->msgtime_mutex);
120                 }else{
121 #ifdef WITH_BROKER
122                         if(mosq->listener){
123                                 mosq->listener->client_count--;
124                                 assert(mosq->listener->client_count >= 0);
125                         }
126                         mosq->listener = NULL;
127 #endif
128                         _mosquitto_socket_close(mosq);
129 #ifndef WITH_BROKER
130                         pthread_mutex_lock(&mosq->state_mutex);
131                         if(mosq->state == mosq_cs_disconnecting){
132                                 rc = MOSQ_ERR_SUCCESS;
133                         }else{
134                                 rc = 1;
135                         }
136                         pthread_mutex_unlock(&mosq->state_mutex);
137                         pthread_mutex_lock(&mosq->callback_mutex);
138                         if(mosq->on_disconnect){
139                                 mosq->in_callback = true;
140                                 mosq->on_disconnect(mosq, mosq->userdata, rc);
141                                 mosq->in_callback = false;
142                         }
143                         pthread_mutex_unlock(&mosq->callback_mutex);
144 #endif
145                 }
146         }
147 }
148
149 uint16_t _mosquitto_mid_generate(struct mosquitto *mosq)
150 {
151         assert(mosq);
152
153         mosq->last_mid++;
154         if(mosq->last_mid == 0) mosq->last_mid++;
155         
156         return mosq->last_mid;
157 }
158
159 /* Search for + or # in a topic. Return MOSQ_ERR_INVAL if found.
160  * Also returns MOSQ_ERR_INVAL if the topic string is too long.
161  * Returns MOSQ_ERR_SUCCESS if everything is fine.
162  */
163 int _mosquitto_topic_wildcard_len_check(const char *str)
164 {
165         int len = 0;
166         while(str && str[0]){
167                 if(str[0] == '+' || str[0] == '#'){
168                         return MOSQ_ERR_INVAL;
169                 }
170                 len++;
171                 str = &str[1];
172         }
173         if(len > 65535) return MOSQ_ERR_INVAL;
174
175         return MOSQ_ERR_SUCCESS;
176 }
177
178 /* Search for + or # in a topic, check they aren't in invalid positions such as foo/#/bar, foo/+bar or foo/bar#.
179  * Return MOSQ_ERR_INVAL if invalid position found.
180  * Also returns MOSQ_ERR_INVAL if the topic string is too long.
181  * Returns MOSQ_ERR_SUCCESS if everything is fine.
182  */
183 int _mosquitto_topic_wildcard_pos_check(const char *str)
184 {
185         char c = '\0';
186         int len = 0;
187         while(str && str[0]){
188                 if(str[0] == '+'){
189                         if((c != '\0' && c != '/') || (str[1] != '\0' && str[1] != '/')){
190                                 return MOSQ_ERR_INVAL;
191                         }
192                 }else if(str[0] == '#'){
193                         if((c != '\0' && c != '/')  || str[1] != '\0'){
194                                 return MOSQ_ERR_INVAL;
195                         }
196                 }
197                 len++;
198                 c = str[0];
199                 str = &str[1];
200         }
201         if(len > 65535) return MOSQ_ERR_INVAL;
202
203         return MOSQ_ERR_SUCCESS;
204 }
205
206 /* Does a topic match a subscription? */
207 int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result)
208 {
209         int slen, tlen;
210         int spos, tpos;
211         bool multilevel_wildcard = false;
212
213         if(!sub || !topic || !result) return MOSQ_ERR_INVAL;
214
215         slen = strlen(sub);
216         tlen = strlen(topic);
217
218         if(slen && tlen){
219                 if((sub[0] == '$' && topic[0] != '$')
220                                 || (topic[0] == '$' && sub[0] != '$')){
221
222                         *result = false;
223                         return MOSQ_ERR_SUCCESS;
224                 }
225         }
226
227         spos = 0;
228         tpos = 0;
229
230         while(spos < slen && tpos < tlen){
231                 if(sub[spos] == topic[tpos]){
232                         spos++;
233                         tpos++;
234                         if(spos == slen && tpos == tlen){
235                                 *result = true;
236                                 return MOSQ_ERR_SUCCESS;
237                         }else if(tpos == tlen && spos == slen-1 && sub[spos] == '+'){
238                                 spos++;
239                                 *result = true;
240                                 return MOSQ_ERR_SUCCESS;
241                         }
242                 }else{
243                         if(sub[spos] == '+'){
244                                 spos++;
245                                 while(tpos < tlen && topic[tpos] != '/'){
246                                         tpos++;
247                                 }
248                                 if(tpos == tlen && spos == slen){
249                                         *result = true;
250                                         return MOSQ_ERR_SUCCESS;
251                                 }
252                         }else if(sub[spos] == '#'){
253                                 multilevel_wildcard = true;
254                                 if(spos+1 != slen){
255                                         *result = false;
256                                         return MOSQ_ERR_SUCCESS;
257                                 }else{
258                                         *result = true;
259                                         return MOSQ_ERR_SUCCESS;
260                                 }
261                         }else{
262                                 *result = false;
263                                 return MOSQ_ERR_SUCCESS;
264                         }
265                 }
266                 if(tpos == tlen-1){
267                         /* Check for e.g. foo matching foo/# */
268                         if(spos == slen-3 
269                                         && sub[spos+1] == '/'
270                                         && sub[spos+2] == '#'){
271                                 *result = true;
272                                 multilevel_wildcard = true;
273                                 return MOSQ_ERR_SUCCESS;
274                         }
275                 }
276         }
277         if(multilevel_wildcard == false && (tpos < tlen || spos < slen)){
278                 *result = false;
279         }
280
281         return MOSQ_ERR_SUCCESS;
282 }
283
284 #ifdef REAL_WITH_TLS_PSK
285 int _mosquitto_hex2bin(const char *hex, unsigned char *bin, int bin_max_len)
286 {
287         BIGNUM *bn = NULL;
288         int len;
289
290         if(BN_hex2bn(&bn, hex) == 0){
291                 if(bn) BN_free(bn);
292                 return 0;
293         }
294         if(BN_num_bytes(bn) > bin_max_len){
295                 BN_free(bn);
296                 return 0;
297         }
298
299         len = BN_bn2bin(bn, bin);
300         BN_free(bn);
301         return len;
302 }
303 #endif
304
305 FILE *_mosquitto_fopen(const char *path, const char *mode)
306 {
307 #ifdef WIN32
308         char buf[MAX_PATH];
309         int rc;
310         rc = ExpandEnvironmentStrings(path, buf, MAX_PATH);
311         if(rc == 0 || rc == MAX_PATH){
312                 return NULL;
313         }else{
314                 return fopen(buf, mode);
315         }
316 #else
317         return fopen(path, mode);
318 #endif
319 }
320