• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  *
24  * MQTT v5
25  *
26  * http://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
27  */
28 
29 #include "private-lib-core.h"
30 /* #include "lws-mqtt.h" */
31 
32 #include <string.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <assert.h>
36 
37 
38 /*
39  * Encode is done into a buffer of at least 4 bytes space.
40  *
41  * Returns -1 for error, or number of bytes used
42  */
43 
44 int
lws_mqtt_vbi_encode(uint32_t value,void * buf)45 lws_mqtt_vbi_encode(uint32_t value, void *buf)
46 {
47 	uint8_t *p = (uint8_t *)buf, b;
48 
49 	if (value > 0xfffffff) {
50 		assert(0);
51 		return -1;
52 	}
53 
54 	do {
55 		b = value & 0x7f;
56 		value >>= 7;
57 		if (value)
58 			*p++ = (0x80 | b);
59 		else
60 			*p++ = b;
61 	} while (value);
62 
63 	return lws_ptr_diff(p, (uint8_t *)buf);
64 }
65 
66 void
lws_mqtt_vbi_init(lws_mqtt_vbi * vbi)67 lws_mqtt_vbi_init(lws_mqtt_vbi *vbi)
68 {
69 	vbi->value = 0;
70 	vbi->consumed = 0;
71 	vbi->budget = 4;
72 }
73 
74 void
lws_mqtt_2byte_init(lws_mqtt_vbi * vbi)75 lws_mqtt_2byte_init(lws_mqtt_vbi *vbi)
76 {
77 	vbi->value = 0;
78 	vbi->consumed = 0;
79 	vbi->budget = 2;
80 }
81 
82 void
lws_mqtt_4byte_init(lws_mqtt_vbi * vbi)83 lws_mqtt_4byte_init(lws_mqtt_vbi *vbi)
84 {
85 	vbi->value = 0;
86 	vbi->consumed = 0;
87 	vbi->budget = 4;
88 }
89 
90 lws_mqtt_stateful_primitive_return_t
lws_mqtt_vbi_r(lws_mqtt_vbi * vbi,const uint8_t ** in,size_t * len)91 lws_mqtt_vbi_r(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len)
92 {
93 	uint8_t multiplier = 0;
94 	if (!vbi->budget) {
95 		lwsl_info("%s: bad vbi\n", __func__);
96 
97 		return LMSPR_FAILED_ALREADY_COMPLETED;
98 	}
99 
100 	while (*len && vbi->budget--) {
101 		uint8_t u = *((*in)++);
102 
103 		(*len)--;
104 		vbi->consumed++;
105 		vbi->value += (u & 0x7f) << multiplier;
106 		multiplier += 7;
107 		if (!(u & 0x80))
108 			return LMSPR_COMPLETED; /* finished */
109 	}
110 
111 	if (!vbi->budget) { /* should have ended on b7 = 0 and exited then... */
112 		lwsl_info("%s: bad vbi\n", __func__);
113 
114 		return LMSPR_FAILED_FORMAT;
115 	}
116 
117 	return LMSPR_NEED_MORE;
118 }
119 
120 lws_mqtt_stateful_primitive_return_t
lws_mqtt_mb_parse(lws_mqtt_vbi * vbi,const uint8_t ** in,size_t * len)121 lws_mqtt_mb_parse(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len)
122 {
123 	if (!vbi->budget)
124 		return LMSPR_FAILED_ALREADY_COMPLETED;
125 
126 	while (*len && vbi->budget--) {
127 		vbi->value = (vbi->value << 8) | *((*in)++);
128 		(*len)--;
129 		vbi->consumed++;
130 	}
131 
132 	return vbi->budget ? LMSPR_NEED_MORE : LMSPR_COMPLETED;
133 }
134 
135 /*
136  * You can leave buf NULL, if so it will be allocated on the heap once the
137  * actual length is known.  nf should be 0, it will be set at allocation time.
138  *
139  * Or you can ensure no allocation and use an external buffer by setting buf
140  * and lim.  But buf must be in the ep context somehow, since it may have to
141  * survive returns to the event loop unchanged.  Set nf to 0 in this case.
142  *
143  * Or you can set buf to an externally allocated buffer, in which case you may
144  * set nf so it will be freed when the string is "freed".
145  */
146 
147 void
lws_mqtt_str_init(lws_mqtt_str_t * s,uint8_t * buf,uint16_t lim,char nf)148 lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf)
149 {
150 	s->len = 0;	/* at COMPLETED, consumed count is s->len + 2 */
151 	s->pos = 0;
152 	s->buf = buf;
153 	s->limit = lim;
154 	s->len_valid = 0;
155 	s->needs_freeing = nf;
156 }
157 
158 lws_mqtt_str_t *
lws_mqtt_str_create(uint16_t lim)159 lws_mqtt_str_create(uint16_t lim)
160 {
161 	lws_mqtt_str_t *s = lws_malloc(sizeof(*s) + lim + 1, __func__);
162 
163 	if (!s)
164 		return NULL;
165 
166 	s->len = 0;
167 	s->pos = 0;
168 	s->buf = (uint8_t *)&s[1];
169 	s->limit = lim;
170 	s->len_valid = 0;
171 	s->needs_freeing = 1;
172 
173 	return s;
174 }
175 
176 lws_mqtt_str_t *
lws_mqtt_str_create_init(uint8_t * buf,uint16_t len,uint16_t lim)177 lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim)
178 {
179 	lws_mqtt_str_t *s;
180 
181 	if (!lim)
182 		lim = len;
183 
184 	s = lws_mqtt_str_create(lim);
185 
186 	if (!s)
187 		return NULL;
188 
189 	memcpy(s->buf, buf, len);
190 	s->len = len;
191 	s->len_valid = 1;
192 	s->pos = len;
193 
194 	return s;
195 }
196 
197 
198 lws_mqtt_str_t *
lws_mqtt_str_create_cstr_dup(const char * buf,uint16_t lim)199 lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim)
200 {
201 	size_t len = strlen(buf);
202 
203 	if (!lim)
204 		lim = (uint16_t)len;
205 
206 	return lws_mqtt_str_create_init((uint8_t *)buf, (uint16_t)len, lim);
207 }
208 
209 uint8_t *
lws_mqtt_str_next(lws_mqtt_str_t * s,uint16_t * budget)210 lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget)
211 {
212 	if (budget)
213 		*budget = s->limit - s->pos;
214 
215 	return &s->buf[s->pos];
216 }
217 
218 int
lws_mqtt_str_advance(lws_mqtt_str_t * s,int n)219 lws_mqtt_str_advance(lws_mqtt_str_t *s, int n)
220 {
221 	if (n > s->limit - s->pos) {
222 		lwsl_err("%s: attempted overflow %d vs %d\n", __func__,
223 			 n, s->limit - s->pos);
224 		return 1;
225 	}
226 
227 	s->pos += n;
228 	s->len += n;
229 
230 	return 0;
231 }
232 
233 void
lws_mqtt_str_free(lws_mqtt_str_t ** ps)234 lws_mqtt_str_free(lws_mqtt_str_t **ps)
235 {
236 	lws_mqtt_str_t *s = *ps;
237 
238 	if (!s || !s->needs_freeing)
239 		return;
240 
241 	/* buf may be independently allocated or allocated along with the
242 	 * lws_mqtt_str_t at the end... if so the whole lws_mqtt_str_t is freed.
243 	 */
244 
245 	if (s->buf != (uint8_t *)&s[1])
246 		lws_free_set_NULL(s->buf);
247 	else
248 		lws_free_set_NULL(*ps);
249 }
250 
251 /*
252  * Parses and allocates for lws_mqtt_str_t in a fragmentation-immune, but
253  * efficient for bulk data way.
254  *
255  * Returns: LMSPR_NEED_MORE if needs more data,
256  * 	    LMSPR_COMPLETED if complete, <0 for error
257  *
258  * *len is reduced by, and *in is advanced by, the amount of data actually used,
259  * except in error case
260  *
261  * lws_mqtt_str_free() must be called after calling this successfully
262  * or not.
263  */
264 lws_mqtt_stateful_primitive_return_t
lws_mqtt_str_parse(lws_mqtt_str_t * s,const uint8_t ** in,size_t * len)265 lws_mqtt_str_parse(lws_mqtt_str_t *s, const uint8_t **in, size_t *len)
266 {
267 	const uint8_t *oin = *in;
268 
269 	/* handle the length + allocation if needed */
270 	while (*len && !s->len_valid && s->pos < 2) {
271 		s->len = (s->len << 8) | *((*in)++);
272 		(*len)--;
273 		oin = *in;
274 		if (++s->pos == 2) {
275 			if (s->len > s->limit)
276 				return LMSPR_FAILED_OVERSIZE;
277 
278 			s->pos = 0;
279 			s->len_valid = 1;
280 
281 			if (!s->len) /* do not need to allocate */
282 				return LMSPR_COMPLETED;
283 
284 			if (!s->buf) {
285 				s->buf = lws_malloc(s->len, __func__);
286 				if (!s->buf)
287 					return LMSPR_FAILED_OOM;
288 
289 				s->needs_freeing = 1;
290 			}
291 		}
292 	}
293 
294 	/* handle copying bulk data into allocation */
295 	if (s->len_valid && *len) {
296 		uint16_t span = s->len - s->pos;
297 
298 		if (span > *len)
299 			span = (uint16_t)*len;
300 
301 		memcpy(s->buf + s->pos, *in, span);
302 		*in += span;
303 		s->pos += span;
304 	}
305 
306 	*len -= *in - oin;
307 
308 	return s->buf && s->pos == s->len ? LMSPR_COMPLETED : LMSPR_NEED_MORE;
309 }
310 
311 int
lws_mqtt_bindata_cmp(const lws_mqtt_str_t * bd1,const lws_mqtt_str_t * bd2)312 lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1,
313 		     const lws_mqtt_str_t *bd2)
314 {
315 	if (bd1->len != bd2->len)
316 		return 1;
317 
318 	if (!!bd1->buf != !!bd2->buf)
319 		return 1;
320 
321 	if (!bd1->buf && !bd2->buf)
322 		return 0;
323 
324 	return memcmp(bd1->buf, bd2->buf, bd1->len);
325 }
326 
327