Annotation of libaitmqtt/src/aitmqtt.c, revision 1.3.4.2
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.2 ! misho 6: * $Id: aitmqtt.c,v 1.3.4.1 2022/09/13 20:12:15 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: #pragma GCC visibility push(hidden)
1.2 misho 84: /* _mqtt_readHEADER() read fixed header from MQTT message */
1.3 misho 85: struct mqtthdr *
1.1 misho 86: _mqtt_readHEADER(mqtt_msg_t * __restrict buf, u_char cmd, int *bytes, int *len)
87: {
88: struct mqtthdr *hdr;
89:
90: if (!buf || !buf->msg_base || !buf->msg_len)
91: return NULL;
92:
93: hdr = (struct mqtthdr*) buf->msg_base;
94: if (hdr->mqtt_msg.type != cmd) {
95: mqtt_SetErr(EINVAL, "Error:: wrong command #%d should be %d",
96: hdr->mqtt_msg.type, cmd);
97: return NULL;
98: }
99:
100: *len = mqtt_decodeLen(hdr->mqtt_len, bytes);
101: return hdr;
102: }
103: #pragma GCC visibility pop
104:
105:
106: /*
107: * mqtt_msgFree() Free MQTT message
108: *
109: * @msg = Message buffer
110: * @all = !=0 Destroy entire message, if MQTT Message allocated with mqtt_msgAlloc()
111: * return: none
112: */
1.3 misho 113: void
1.1 misho 114: mqtt_msgFree(mqtt_msg_t ** __restrict msg, int all)
115: {
116: if (msg && *msg) {
117: if ((*msg)->msg_base) {
118: free((*msg)->msg_base);
119: (*msg)->msg_base = NULL;
120: }
121: if (all) {
122: free(*msg);
123: *msg = NULL;
124: } else
125: (*msg)->msg_len ^= (*msg)->msg_len;
126: }
127: }
128:
129: /*
130: * mqtt_msgAlloc() Allocate memory for MQTT Message
131: *
132: * @len = >0 Allocate buffer with length
133: * return: NULL error or Message, after use must call mqtt_msgFree() with all!=0
134: */
1.3 misho 135: mqtt_msg_t *
1.3.4.1 misho 136: mqtt_msgAlloc(u_int len)
1.1 misho 137: {
138: mqtt_msg_t *m = NULL;
139:
140: m = malloc(sizeof(mqtt_msg_t));
141: if (!m) {
142: LOGERR;
143: return NULL;
144: } else
145: memset(m, 0, sizeof(mqtt_msg_t));
146:
147: if (len) {
148: m->msg_len = len;
149: m->msg_base = malloc(m->msg_len);
150: if (!m->msg_base) {
151: LOGERR;
152: free(m);
153: return NULL;
154: } else
155: memset(m->msg_base, 0, m->msg_len);
156: }
157:
158: return m;
159: }
160:
161: /*
162: * mqtt_msgRealloc() Reallocate MQTT message buffer
163: *
164: * @msg = MQTT message
165: * @len = new length
166: * return: -1 error or >-1 old buffer length
167: */
1.3 misho 168: int
1.3.4.1 misho 169: mqtt_msgRealloc(mqtt_msg_t * __restrict msg, u_int len)
1.1 misho 170: {
171: void *p = NULL;
172: int ret = 0;
173:
174: if (!msg)
175: return -1;
176:
1.2 misho 177: if (len <= msg->msg_len)
1.1 misho 178: return len;
179:
180: p = realloc(msg->msg_base, len);
181: if (!p) {
182: LOGERR;
183: return -1;
184: }
185:
186: ret = msg->msg_len;
187: msg->msg_len = len;
188: msg->msg_base = p;
189:
190: return ret;
191: }
192:
193: /*
1.2 misho 194: * mqtt_msgDup() - Duplicate message buffer
195: *
196: * @msg = Message
197: * return: NULL error or !=NULL duplicated message, after use must call mqtt_msgFree() with all!=0
198: */
1.3 misho 199: mqtt_msg_t *
1.2 misho 200: mqtt_msgDup(mqtt_msg_t * __restrict msg)
201: {
202: mqtt_msg_t *m = NULL;
203:
204: m = malloc(sizeof(mqtt_msg_t));
205: if (!m) {
206: LOGERR;
207: return NULL;
208: } else
209: memset(m, 0, sizeof(mqtt_msg_t));
210:
211: if (msg->msg_len) {
212: m->msg_len = msg->msg_len;
213: m->msg_base = malloc(m->msg_len);
214: if (!m->msg_base) {
215: LOGERR;
216: free(m);
217: return NULL;
218: } else
219: memcpy(m->msg_base, msg->msg_base, m->msg_len);
220: }
221:
222: return m;
223: }
224:
225: /*
1.1 misho 226: * mqtt_encodeLen() Encode number to MQTT length field
227: *
228: * @num = number for encode
229: * return: -1 error or >-1 length
230: */
1.3 misho 231: u_int
1.1 misho 232: mqtt_encodeLen(u_int num)
233: {
234: register u_int dig, i;
235: u_int ret = 0;
236:
1.3.4.1 misho 237: if (num > MQTT_DATA_MAX)
1.1 misho 238: return (u_int) -1;
239:
240: for (i = 0; i < sizeof ret && num > 0; i++) {
241: dig = num % 0x80;
242: num /= 0x80;
243: if (num > 0)
244: dig |= 0x80;
245:
246: *((u_char*) &ret + i) = (u_char) dig;
247: }
248:
249: return ret;
250: }
251:
252: /*
253: * mqtt_decodeLen() Decode length from MQTT packet
254: *
255: * @len = length from MQTT header
256: * @n = sizeof bytes, if !=NULL
257: * return: -1 error, >-1 length of message
258: */
1.3 misho 259: u_int
1.1 misho 260: mqtt_decodeLen(void * __restrict len, int * __restrict n)
261: {
262: register u_int i, dig, mul;
263: u_int ret = 0;
264: u_char *p = (u_char*) len;
265:
266: if (!len)
267: return (u_int) -1;
268:
269: for (mul = 1, i = 0; i < sizeof ret; i++, mul *= 0x80) {
270: dig = p[i];
271: ret += (dig & 0x7f) * mul;
272:
273: if (!(dig & 0x80))
274: break;
275: }
276:
277: if (n)
278: *n = (char) (i & 0x7f) + 1;
1.3.4.1 misho 279:
1.1 misho 280: return ret;
281: }
282:
283: /*
284: * mqtt_sizeLen Return sizeof len field
285: *
286: * @len = length
287: * return: -1 error, >-1 sizeof len in bytes
288: */
1.3 misho 289: char
1.1 misho 290: mqtt_sizeLen(u_int len)
291: {
292: register char i;
293: u_char *p = (u_char*) &len;
294:
295: if (len > 0xffffff7f)
296: return -1;
297:
298: for (i = 0; i < sizeof len; i++)
299: if (!(*(p + i) & 0x80))
300: break;
301:
302: return ++i;
303: }
304:
305: /*
1.2 misho 306: * mqtt_pktLen() - Get total packet length
307: *
308: * @hdr = MQTT packet header
309: * return: packet length
310: */
1.3 misho 311: u_int
1.2 misho 312: mqtt_pktLen(struct mqtthdr * __restrict hdr)
313: {
314: int siz, n = 0;
315:
316: if (!hdr)
317: return 0;
318:
319: siz = mqtt_decodeLen(hdr->mqtt_len, &n);
320: siz += sizeof(struct mqtthdr) + n - 1;
321:
322: return siz;
323: }
324:
325: /*
326: * mqtt_str2subs Create MQTT subscribe variable from string(s)
1.1 misho 327: *
1.2 misho 328: * @csStr = null terminated string array
1.3.4.1 misho 329: * @strnum = copy at most number of strings elements, ==0 till NULL element
1.1 misho 330: * @qoses = QoS elements applied to subscribe variable,
331: * count of elements must be equal with csStr elements
332: * return: NULL error or != subscribe variables array, must be free after use with mqtt_freeSub()
333: */
1.3 misho 334: mqtt_subscr_t *
1.3.4.1 misho 335: mqtt_strs2subs(const char **csStr, u_short strnum, u_char *qoses)
1.1 misho 336: {
337: mqtt_subscr_t *v;
338: register int i, items;
339: const char **strs;
340:
341: if (!csStr)
342: return NULL;
1.2 misho 343:
344: for (items = 0, strs = csStr;
345: (!strnum || (strnum && items < strnum)) && *strs;
346: items++, strs++);
1.1 misho 347:
348: if (!(v = malloc((items + 1) * sizeof(mqtt_subscr_t)))) {
349: LOGERR;
350: return NULL;
351: } else
352: memset(v, 0, (items + 1) * sizeof(mqtt_subscr_t));
353:
354: for (i = 0; i < items; i++) {
355: v[i].sub_topic.msg_len = strlen(csStr[i]);
356: v[i].sub_topic.msg_base = (u_char*) strdup(csStr[i]);
357: if (qoses && qoses[i] < MQTT_QOS_RESERVED)
358: v[i].sub_ret = qoses[i];
359: }
360:
361: return v;
362: }
363:
364: /*
365: * mqtt_subFree() Free array from subscribe variables
366: *
367: * @subs = Subscribe variables
368: * return: none
369: */
1.3 misho 370: void
1.1 misho 371: mqtt_subFree(mqtt_subscr_t ** __restrict subs)
372: {
373: mqtt_subscr_t *v;
374:
375: if (!subs)
376: return;
377:
378: for (v = *subs; v->sub_topic.msg_base; v++) {
379: free(v->sub_topic.msg_base);
380: v->sub_topic.msg_base = NULL;
381: v->sub_topic.msg_len = 0;
382:
383: if (v->sub_value.msg_base) {
384: free(v->sub_value.msg_base);
385: v->sub_value.msg_base = NULL;
386: v->sub_value.msg_len = 0;
387: }
388: }
389:
390: free(*subs);
391: *subs = NULL;
392: }
393:
394: /*
395: * mqtt_subAlloc() Create array from subscribe variables
396: *
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.1 misho 401: mqtt_subAlloc(u_short num)
402: {
403: mqtt_subscr_t *s = NULL;
404:
405: s = malloc((num + 1) * sizeof(mqtt_subscr_t));
406: if (!s) {
407: LOGERR;
408: return NULL;
409: } else
410: memset(s, 0, (num + 1) * sizeof(mqtt_subscr_t));
411:
412: return s;
413: }
414:
415: /*
416: * mqtt_subRealloc() Reallocate array from subscribe variables
417: *
418: * @subs = Subscribe array
419: * @num = Number of elements
420: * return: NULL error or subscribe array, after use must call mqtt_subFree()
421: */
1.3 misho 422: mqtt_subscr_t *
1.2 misho 423: mqtt_subRealloc(mqtt_subscr_t ** __restrict subs, u_short num)
1.1 misho 424: {
1.3.4.2 ! misho 425: mqtt_subscr_t *ss, *s = NULL;
1.3.4.1 misho 426: register int i;
1.1 misho 427:
1.2 misho 428: if (!subs)
429: return NULL;
430:
1.3.4.2 ! misho 431: for (i = 0, ss = *subs; ss; i++, ss++);
1.3.4.1 misho 432: if (i < num)
433: return NULL;
434: if (i == num)
435: return *subs;
436:
1.2 misho 437: s = realloc(*subs, (num + 1) * sizeof(mqtt_subscr_t));
1.1 misho 438: if (!s) {
439: LOGERR;
440: return NULL;
1.2 misho 441: } else {
442: memset(s + num, 0, sizeof(mqtt_subscr_t));
443: *subs = s;
444: }
445:
446: return *subs;
447: }
448:
449: /*
450: * mqtt_subCopy() - Copy subscription structure to another one
451: *
452: * @dst = destination subscription
453: * @src = source subscription
454: * return: =NULL error or !=NULL successful copied a structure
455: */
1.3 misho 456: mqtt_subscr_t *
1.2 misho 457: mqtt_subCopy(mqtt_subscr_t * __restrict dst, mqtt_subscr_t * __restrict src)
458: {
459: if (!dst || !src)
460: return NULL;
461:
462: if (src->sub_topic.msg_base) {
463: dst->sub_topic.msg_base = malloc(src->sub_topic.msg_len + 1);
464: if (!dst->sub_topic.msg_base) {
465: LOGERR;
466: memset(dst, 0, sizeof(mqtt_subscr_t));
467: return NULL;
468: } else {
469: dst->sub_topic.msg_len = src->sub_topic.msg_len;
470: ((char*) dst->sub_topic.msg_base)[dst->sub_topic.msg_len] = 0;
471: memcpy(dst->sub_topic.msg_base, src->sub_topic.msg_base,
472: dst->sub_topic.msg_len);
473: }
474: } else {
475: dst->sub_topic.msg_base = NULL;
476: dst->sub_topic.msg_len = 0;
477: }
1.3.4.1 misho 478:
1.2 misho 479: if (src->sub_value.msg_base) {
480: dst->sub_value.msg_base = malloc(src->sub_value.msg_len + 1);
481: if (!dst->sub_value.msg_base) {
482: LOGERR;
483: if (dst->sub_topic.msg_base)
484: free(dst->sub_topic.msg_base);
485: memset(dst, 0, sizeof(mqtt_subscr_t));
486: return NULL;
487: } else {
488: dst->sub_value.msg_len = src->sub_value.msg_len;
489: ((char*) dst->sub_value.msg_base)[dst->sub_value.msg_len] = 0;
490: memcpy(dst->sub_value.msg_base, src->sub_value.msg_base,
491: dst->sub_value.msg_len);
492: }
493: } else {
494: dst->sub_value.msg_base = NULL;
495: dst->sub_value.msg_len = 0;
496: }
497:
498: dst->sub_ret = src->sub_ret;
499: return dst;
500: }
501:
502:
503: /*
504: * mqtt_expandTopic() - Expanding topic to regular expression
505: *
506: * @csInput = Input topic
507: * @psRegEx = Output to regular expression
508: * @regexLen = Length of psRegEx
509: * @BOL = Begin of Line, if =0 not added
510: * @EOL = End of Line, if =0 not appended
511: * return: -1 error, 0 nothing expanded or >0 expanded bytes
512: */
513: int
514: mqtt_expandTopic(const char *csInput, char * __restrict psRegEx, int regexLen, u_char BOL, u_char EOL)
515: {
516: int ret = 0;
517: register int i;
518: char *pos, *s;
519: const char reROM[] = "[](){}^$\\-|?.+*";
520:
521: if (!csInput || !psRegEx || regexLen < 1)
522: return -1;
523: else
524: memset(psRegEx, 0, regexLen);
525:
526: /* check # */
527: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
528: if (*pos == '#')
529: i++;
530: if (i == 2) {
531: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
532: return -1;
533: }
534: if (i == 1 && (pos = strrchr(csInput, '#')))
535: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
536: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
537: return -1;
538: }
539: /* check + */
540: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
541: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
542: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
543: return -1;
544: }
545:
546: /* BUILD REGEX */
547: s = psRegEx;
548: if (BOL) {
549: *s++ = '^';
550: ret++;
551: }
552: for (pos = (char*) csInput; s < psRegEx + regexLen && *pos; s++, pos++) {
553: if (*pos == '#') {
554: strlcat(s, ".*", regexLen - (s - psRegEx));
555: s++;
556: ret++;
557: break;
558: }
559: if (*pos == '+') {
560: if (*(pos + 1)) {
561: strlcat(s, ".*", regexLen - (s - psRegEx));
562: s++;
563: ret++;
564: continue;
565: } else {
566: strlcat(s, ".*/", regexLen - (s - psRegEx));
567: ret += 2;
568: break;
569: }
570: }
571: for (i = 0; i < sizeof reROM - 1; i++)
572: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
573: *s++ = '\\';
574: ret++;
575: break;
576: }
577:
578: *s = *pos;
1.1 misho 579: }
1.2 misho 580: if (EOL) {
581: strlcat(psRegEx, "$", regexLen);
582: ret++;
583: }
584:
585: return ret;
586: }
587:
588: /*
589: * mqtt_sqlTopic() - Expanding topic to SQL search string
590: *
591: * @csInput = Input topic
592: * @psSQL = Output to SQL search string
593: * @sqlLen = Length of psSQL
594: * return: -1 error, 0 changed bytes
595: */
596: int
597: mqtt_sqlTopic(const char *csInput, char * __restrict psSQL, int sqlLen)
598: {
599: int ret = 0;
600: register int i;
601: char *pos, *s;
602:
603: if (!csInput || !psSQL || sqlLen < 1)
604: return -1;
605: else
606: memset(psSQL, 0, sqlLen);
1.1 misho 607:
1.2 misho 608: /* check # */
609: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
610: if (*pos == '#')
611: i++;
612: if (i == 2) {
613: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
614: return -1;
615: }
616: if (i == 1 && (pos = strrchr(csInput, '#')))
617: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
618: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
619: return -1;
620: }
621: /* check + */
622: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
623: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
624: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
625: return -1;
626: }
627:
628: /* BUILD SEARCH STRING */
629: s = psSQL;
630: for (pos = (char*) csInput; s < psSQL + sqlLen && *pos; s++, pos++) {
631: if (*pos == '#') {
632: *s = '%';
633: s++;
634: ret++;
635: break;
636: }
637: if (*pos == '+') {
638: if (*(pos + 1)) {
639: *s = '%';
640: ret++;
641: continue;
642: } else {
643: strlcat(s, "%/", sqlLen - (s - psSQL));
644: ret += 2;
645: break;
646: }
647: }
648: /*
649: for (i = 0; i < sizeof reROM - 1; i++)
650: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
651: *s++ = '\\';
652: ret++;
653: break;
654: }
655: */
656:
657: *s = *pos;
658: }
659:
660: return ret;
1.1 misho 661: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>