Annotation of libaitmqtt/src/aitmqtt.c, revision 1.3.4.5
1.1 misho 1: /*************************************************************************
2: * (C) 2011 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
1.3.4.5 ! misho 6: * $Id: aitmqtt.c,v 1.3.4.4 2022/09/14 14:32:48 misho Exp $
1.1 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
1.3.4.2 misho 15: Copyright 2004 - 2022
1.1 misho 16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: #pragma GCC visibility push(hidden)
50:
51: int mqtt_Errno;
52: char mqtt_Error[STRSIZ];
53:
54: #pragma GCC visibility pop
55:
56: // mqtt_GetErrno() Get error code of last operation
1.3 misho 57: int
1.1 misho 58: mqtt_GetErrno()
59: {
60: return mqtt_Errno;
61: }
62:
63: // mqtt_GetError() Get error text of last operation
1.3 misho 64: const char *
1.1 misho 65: mqtt_GetError()
66: {
67: return mqtt_Error;
68: }
69:
70: // mqtt_SetErr() Set error to variables for internal use!!!
1.3 misho 71: void
1.1 misho 72: mqtt_SetErr(int eno, char *estr, ...)
73: {
74: va_list lst;
75:
76: mqtt_Errno = eno;
77: memset(mqtt_Error, 0, sizeof mqtt_Error);
78: va_start(lst, estr);
79: vsnprintf(mqtt_Error, sizeof mqtt_Error, estr, lst);
80: va_end(lst);
81: }
82:
83:
84: /*
85: * mqtt_msgFree() Free MQTT message
86: *
87: * @msg = Message buffer
1.3.4.3 misho 88: * @keepmsg = !=0 just free message content
1.1 misho 89: * return: none
90: */
1.3 misho 91: void
1.3.4.3 misho 92: mqtt_msgFree(mqtt_msg_t ** __restrict msg, int keepmsg)
1.1 misho 93: {
94: if (msg && *msg) {
95: if ((*msg)->msg_base) {
1.3.4.4 misho 96: e_free((*msg)->msg_base);
1.1 misho 97: (*msg)->msg_base = NULL;
98: }
1.3.4.3 misho 99: if (!keepmsg) {
1.3.4.4 misho 100: e_free(*msg);
1.1 misho 101: *msg = NULL;
102: } else
103: (*msg)->msg_len ^= (*msg)->msg_len;
104: }
105: }
106:
107: /*
108: * mqtt_msgAlloc() Allocate memory for MQTT Message
109: *
110: * @len = >0 Allocate buffer with length
111: * return: NULL error or Message, after use must call mqtt_msgFree() with all!=0
112: */
1.3 misho 113: mqtt_msg_t *
1.3.4.1 misho 114: mqtt_msgAlloc(u_int len)
1.1 misho 115: {
116: mqtt_msg_t *m = NULL;
117:
1.3.4.4 misho 118: m = e_malloc(sizeof(mqtt_msg_t));
1.1 misho 119: if (!m) {
120: LOGERR;
121: return NULL;
122: } else
123: memset(m, 0, sizeof(mqtt_msg_t));
124:
125: if (len) {
126: m->msg_len = len;
1.3.4.4 misho 127: m->msg_base = e_malloc(m->msg_len);
1.1 misho 128: if (!m->msg_base) {
129: LOGERR;
1.3.4.4 misho 130: e_free(m);
1.1 misho 131: return NULL;
132: } else
133: memset(m->msg_base, 0, m->msg_len);
134: }
135:
136: return m;
137: }
138:
139: /*
140: * mqtt_msgRealloc() Reallocate MQTT message buffer
141: *
142: * @msg = MQTT message
143: * @len = new length
144: * return: -1 error or >-1 old buffer length
145: */
1.3 misho 146: int
1.3.4.1 misho 147: mqtt_msgRealloc(mqtt_msg_t * __restrict msg, u_int len)
1.1 misho 148: {
149: void *p = NULL;
150: int ret = 0;
151:
152: if (!msg)
153: return -1;
154:
1.2 misho 155: if (len <= msg->msg_len)
1.1 misho 156: return len;
157:
1.3.4.4 misho 158: p = e_realloc(msg->msg_base, len);
1.1 misho 159: if (!p) {
160: LOGERR;
161: return -1;
162: }
163:
164: ret = msg->msg_len;
165: msg->msg_len = len;
166: msg->msg_base = p;
167:
168: return ret;
169: }
170:
171: /*
1.2 misho 172: * mqtt_msgDup() - Duplicate message buffer
173: *
174: * @msg = Message
175: * return: NULL error or !=NULL duplicated message, after use must call mqtt_msgFree() with all!=0
176: */
1.3 misho 177: mqtt_msg_t *
1.2 misho 178: mqtt_msgDup(mqtt_msg_t * __restrict msg)
179: {
180: mqtt_msg_t *m = NULL;
181:
1.3.4.4 misho 182: m = e_malloc(sizeof(mqtt_msg_t));
1.2 misho 183: if (!m) {
184: LOGERR;
185: return NULL;
186: } else
187: memset(m, 0, sizeof(mqtt_msg_t));
188:
189: if (msg->msg_len) {
190: m->msg_len = msg->msg_len;
1.3.4.4 misho 191: m->msg_base = e_malloc(m->msg_len);
1.2 misho 192: if (!m->msg_base) {
193: LOGERR;
1.3.4.4 misho 194: e_free(m);
1.2 misho 195: return NULL;
196: } else
197: memcpy(m->msg_base, msg->msg_base, m->msg_len);
198: }
199:
200: return m;
201: }
202:
203: /*
1.1 misho 204: * mqtt_encodeLen() Encode number to MQTT length field
205: *
206: * @num = number for encode
207: * return: -1 error or >-1 length
208: */
1.3 misho 209: u_int
1.1 misho 210: mqtt_encodeLen(u_int num)
211: {
212: register u_int dig, i;
213: u_int ret = 0;
214:
1.3.4.1 misho 215: if (num > MQTT_DATA_MAX)
1.1 misho 216: return (u_int) -1;
217:
218: for (i = 0; i < sizeof ret && num > 0; i++) {
219: dig = num % 0x80;
220: num /= 0x80;
221: if (num > 0)
222: dig |= 0x80;
223:
224: *((u_char*) &ret + i) = (u_char) dig;
225: }
226:
227: return ret;
228: }
229:
230: /*
231: * mqtt_decodeLen() Decode length from MQTT packet
232: *
233: * @len = length from MQTT header
234: * @n = sizeof bytes, if !=NULL
235: * return: -1 error, >-1 length of message
236: */
1.3 misho 237: u_int
1.1 misho 238: mqtt_decodeLen(void * __restrict len, int * __restrict n)
239: {
240: register u_int i, dig, mul;
241: u_int ret = 0;
242: u_char *p = (u_char*) len;
243:
244: if (!len)
245: return (u_int) -1;
246:
247: for (mul = 1, i = 0; i < sizeof ret; i++, mul *= 0x80) {
248: dig = p[i];
249: ret += (dig & 0x7f) * mul;
250:
251: if (!(dig & 0x80))
252: break;
253: }
254:
255: if (n)
256: *n = (char) (i & 0x7f) + 1;
1.3.4.1 misho 257:
1.1 misho 258: return ret;
259: }
260:
261: /*
262: * mqtt_sizeLen Return sizeof len field
263: *
264: * @len = length
265: * return: -1 error, >-1 sizeof len in bytes
266: */
1.3 misho 267: char
1.1 misho 268: mqtt_sizeLen(u_int len)
269: {
270: register char i;
271: u_char *p = (u_char*) &len;
272:
273: if (len > 0xffffff7f)
274: return -1;
275:
276: for (i = 0; i < sizeof len; i++)
277: if (!(*(p + i) & 0x80))
278: break;
279:
280: return ++i;
281: }
282:
283: /*
1.2 misho 284: * mqtt_pktLen() - Get total packet length
285: *
286: * @hdr = MQTT packet header
287: * return: packet length
288: */
1.3 misho 289: u_int
1.2 misho 290: mqtt_pktLen(struct mqtthdr * __restrict hdr)
291: {
292: int siz, n = 0;
293:
294: if (!hdr)
295: return 0;
296:
297: siz = mqtt_decodeLen(hdr->mqtt_len, &n);
298: siz += sizeof(struct mqtthdr) + n - 1;
299:
300: return siz;
301: }
302:
303: /*
304: * mqtt_str2subs Create MQTT subscribe variable from string(s)
1.1 misho 305: *
1.2 misho 306: * @csStr = null terminated string array
1.3.4.1 misho 307: * @strnum = copy at most number of strings elements, ==0 till NULL element
1.1 misho 308: * @qoses = QoS elements applied to subscribe variable,
309: * count of elements must be equal with csStr elements
310: * return: NULL error or != subscribe variables array, must be free after use with mqtt_freeSub()
311: */
1.3 misho 312: mqtt_subscr_t *
1.3.4.1 misho 313: mqtt_strs2subs(const char **csStr, u_short strnum, u_char *qoses)
1.1 misho 314: {
315: mqtt_subscr_t *v;
316: register int i, items;
317: const char **strs;
318:
319: if (!csStr)
320: return NULL;
1.2 misho 321:
322: for (items = 0, strs = csStr;
323: (!strnum || (strnum && items < strnum)) && *strs;
324: items++, strs++);
1.1 misho 325:
1.3.4.4 misho 326: if (!(v = e_malloc((items + 1) * sizeof(mqtt_subscr_t)))) {
1.1 misho 327: LOGERR;
328: return NULL;
329: } else
330: memset(v, 0, (items + 1) * sizeof(mqtt_subscr_t));
331:
332: for (i = 0; i < items; i++) {
333: v[i].sub_topic.msg_len = strlen(csStr[i]);
1.3.4.4 misho 334: v[i].sub_topic.msg_base = (u_char*) e_strdup(csStr[i]);
1.1 misho 335: if (qoses && qoses[i] < MQTT_QOS_RESERVED)
1.3.4.5 ! misho 336: v[i].sub_qos = qoses[i];
1.1 misho 337: }
338:
339: return v;
340: }
341:
342: /*
343: * mqtt_subFree() Free array from subscribe variables
344: *
345: * @subs = Subscribe variables
346: * return: none
347: */
1.3 misho 348: void
1.1 misho 349: mqtt_subFree(mqtt_subscr_t ** __restrict subs)
350: {
351: mqtt_subscr_t *v;
352:
353: if (!subs)
354: return;
355:
356: for (v = *subs; v->sub_topic.msg_base; v++) {
1.3.4.4 misho 357: e_free(v->sub_topic.msg_base);
1.1 misho 358: v->sub_topic.msg_base = NULL;
359: v->sub_topic.msg_len = 0;
360:
361: if (v->sub_value.msg_base) {
1.3.4.4 misho 362: e_free(v->sub_value.msg_base);
1.1 misho 363: v->sub_value.msg_base = NULL;
364: v->sub_value.msg_len = 0;
365: }
366: }
367:
1.3.4.4 misho 368: e_free(*subs);
1.1 misho 369: *subs = NULL;
370: }
371:
372: /*
373: * mqtt_subAlloc() Create array from subscribe variables
374: *
375: * @num = Number of elements
376: * return: NULL error or subscribe array, after use must call mqtt_subFree()
377: */
1.3 misho 378: mqtt_subscr_t *
1.1 misho 379: mqtt_subAlloc(u_short num)
380: {
381: mqtt_subscr_t *s = NULL;
382:
1.3.4.4 misho 383: s = e_malloc((num + 1) * sizeof(mqtt_subscr_t));
1.1 misho 384: if (!s) {
385: LOGERR;
386: return NULL;
387: } else
388: memset(s, 0, (num + 1) * sizeof(mqtt_subscr_t));
389:
390: return s;
391: }
392:
393: /*
394: * mqtt_subRealloc() Reallocate array from subscribe variables
395: *
396: * @subs = Subscribe array
397: * @num = Number of elements
398: * return: NULL error or subscribe array, after use must call mqtt_subFree()
399: */
1.3 misho 400: mqtt_subscr_t *
1.2 misho 401: mqtt_subRealloc(mqtt_subscr_t ** __restrict subs, u_short num)
1.1 misho 402: {
1.3.4.2 misho 403: mqtt_subscr_t *ss, *s = NULL;
1.3.4.1 misho 404: register int i;
1.1 misho 405:
1.2 misho 406: if (!subs)
407: return NULL;
408:
1.3.4.2 misho 409: for (i = 0, ss = *subs; ss; i++, ss++);
1.3.4.1 misho 410: if (i < num)
411: return NULL;
412: if (i == num)
413: return *subs;
414:
1.3.4.4 misho 415: s = e_realloc(*subs, (num + 1) * sizeof(mqtt_subscr_t));
1.1 misho 416: if (!s) {
417: LOGERR;
418: return NULL;
1.2 misho 419: } else {
420: memset(s + num, 0, sizeof(mqtt_subscr_t));
421: *subs = s;
422: }
423:
424: return *subs;
425: }
426:
427: /*
428: * mqtt_subCopy() - Copy subscription structure to another one
429: *
430: * @dst = destination subscription
431: * @src = source subscription
432: * return: =NULL error or !=NULL successful copied a structure
433: */
1.3 misho 434: mqtt_subscr_t *
1.2 misho 435: mqtt_subCopy(mqtt_subscr_t * __restrict dst, mqtt_subscr_t * __restrict src)
436: {
437: if (!dst || !src)
438: return NULL;
439:
440: if (src->sub_topic.msg_base) {
1.3.4.4 misho 441: dst->sub_topic.msg_base = e_malloc(src->sub_topic.msg_len + 1);
1.2 misho 442: if (!dst->sub_topic.msg_base) {
443: LOGERR;
444: memset(dst, 0, sizeof(mqtt_subscr_t));
445: return NULL;
446: } else {
447: dst->sub_topic.msg_len = src->sub_topic.msg_len;
448: ((char*) dst->sub_topic.msg_base)[dst->sub_topic.msg_len] = 0;
449: memcpy(dst->sub_topic.msg_base, src->sub_topic.msg_base,
450: dst->sub_topic.msg_len);
451: }
452: } else {
453: dst->sub_topic.msg_base = NULL;
454: dst->sub_topic.msg_len = 0;
455: }
1.3.4.1 misho 456:
1.2 misho 457: if (src->sub_value.msg_base) {
1.3.4.4 misho 458: dst->sub_value.msg_base = e_malloc(src->sub_value.msg_len + 1);
1.2 misho 459: if (!dst->sub_value.msg_base) {
460: LOGERR;
461: if (dst->sub_topic.msg_base)
1.3.4.4 misho 462: e_free(dst->sub_topic.msg_base);
1.2 misho 463: memset(dst, 0, sizeof(mqtt_subscr_t));
464: return NULL;
465: } else {
466: dst->sub_value.msg_len = src->sub_value.msg_len;
467: ((char*) dst->sub_value.msg_base)[dst->sub_value.msg_len] = 0;
468: memcpy(dst->sub_value.msg_base, src->sub_value.msg_base,
469: dst->sub_value.msg_len);
470: }
471: } else {
472: dst->sub_value.msg_base = NULL;
473: dst->sub_value.msg_len = 0;
474: }
475:
1.3.4.5 ! misho 476: dst->sub_qos = src->sub_qos;
1.2 misho 477: return dst;
478: }
479:
480:
481: /*
482: * mqtt_expandTopic() - Expanding topic to regular expression
483: *
484: * @csInput = Input topic
485: * @psRegEx = Output to regular expression
486: * @regexLen = Length of psRegEx
487: * @BOL = Begin of Line, if =0 not added
488: * @EOL = End of Line, if =0 not appended
489: * return: -1 error, 0 nothing expanded or >0 expanded bytes
490: */
491: int
492: mqtt_expandTopic(const char *csInput, char * __restrict psRegEx, int regexLen, u_char BOL, u_char EOL)
493: {
494: int ret = 0;
495: register int i;
496: char *pos, *s;
497: const char reROM[] = "[](){}^$\\-|?.+*";
498:
499: if (!csInput || !psRegEx || regexLen < 1)
500: return -1;
501: else
502: memset(psRegEx, 0, regexLen);
503:
504: /* check # */
505: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
506: if (*pos == '#')
507: i++;
508: if (i == 2) {
509: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
510: return -1;
511: }
512: if (i == 1 && (pos = strrchr(csInput, '#')))
513: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
514: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
515: return -1;
516: }
517: /* check + */
518: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
519: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
520: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
521: return -1;
522: }
523:
524: /* BUILD REGEX */
525: s = psRegEx;
526: if (BOL) {
527: *s++ = '^';
528: ret++;
529: }
530: for (pos = (char*) csInput; s < psRegEx + regexLen && *pos; s++, pos++) {
531: if (*pos == '#') {
532: strlcat(s, ".*", regexLen - (s - psRegEx));
533: s++;
534: ret++;
535: break;
536: }
537: if (*pos == '+') {
538: if (*(pos + 1)) {
539: strlcat(s, ".*", regexLen - (s - psRegEx));
540: s++;
541: ret++;
542: continue;
543: } else {
544: strlcat(s, ".*/", regexLen - (s - psRegEx));
545: ret += 2;
546: break;
547: }
548: }
549: for (i = 0; i < sizeof reROM - 1; i++)
550: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
551: *s++ = '\\';
552: ret++;
553: break;
554: }
555:
556: *s = *pos;
1.1 misho 557: }
1.2 misho 558: if (EOL) {
559: strlcat(psRegEx, "$", regexLen);
560: ret++;
561: }
562:
563: return ret;
564: }
565:
566: /*
567: * mqtt_sqlTopic() - Expanding topic to SQL search string
568: *
569: * @csInput = Input topic
570: * @psSQL = Output to SQL search string
571: * @sqlLen = Length of psSQL
572: * return: -1 error, 0 changed bytes
573: */
574: int
575: mqtt_sqlTopic(const char *csInput, char * __restrict psSQL, int sqlLen)
576: {
577: int ret = 0;
578: register int i;
579: char *pos, *s;
580:
581: if (!csInput || !psSQL || sqlLen < 1)
582: return -1;
583: else
584: memset(psSQL, 0, sqlLen);
1.1 misho 585:
1.2 misho 586: /* check # */
587: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
588: if (*pos == '#')
589: i++;
590: if (i == 2) {
591: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
592: return -1;
593: }
594: if (i == 1 && (pos = strrchr(csInput, '#')))
595: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
596: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
597: return -1;
598: }
599: /* check + */
600: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
601: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
602: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
603: return -1;
604: }
605:
606: /* BUILD SEARCH STRING */
607: s = psSQL;
608: for (pos = (char*) csInput; s < psSQL + sqlLen && *pos; s++, pos++) {
609: if (*pos == '#') {
610: *s = '%';
611: s++;
612: ret++;
613: break;
614: }
615: if (*pos == '+') {
616: if (*(pos + 1)) {
617: *s = '%';
618: ret++;
619: continue;
620: } else {
621: strlcat(s, "%/", sqlLen - (s - psSQL));
622: ret += 2;
623: break;
624: }
625: }
626: /*
627: for (i = 0; i < sizeof reROM - 1; i++)
628: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
629: *s++ = '\\';
630: ret++;
631: break;
632: }
633: */
634:
635: *s = *pos;
636: }
637:
638: return ret;
1.1 misho 639: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>