Annotation of libaitmqtt/src/aitmqtt.c, revision 1.1.1.1.2.15
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.1.1.1.2.15! misho 6: * $Id: aitmqtt.c,v 1.1.1.1.2.14 2012/05/05 13:10:24 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.1.1.1.2.3 misho 15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
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
57: inline int
58: mqtt_GetErrno()
59: {
60: return mqtt_Errno;
61: }
62:
63: // mqtt_GetError() Get error text of last operation
64: inline const char *
65: mqtt_GetError()
66: {
67: return mqtt_Error;
68: }
69:
70: // mqtt_SetErr() Set error to variables for internal use!!!
71: inline void
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.1.1.1.2.4 misho 84: /* _mqtt_readHEADER() read fixed header from MQTT message */
1.1 misho 85: inline struct mqtthdr *
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: */
113: inline void
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: */
135: inline mqtt_msg_t *
136: mqtt_msgAlloc(u_short len)
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: */
168: inline int
169: mqtt_msgRealloc(mqtt_msg_t * __restrict msg, u_short len)
170: {
171: void *p = NULL;
172: int ret = 0;
173:
174: if (!msg)
175: return -1;
176:
1.1.1.1.2.5 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.1.1.1.2.15! 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: */
! 199: inline mqtt_msg_t *
! 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: */
231: inline u_int
232: mqtt_encodeLen(u_int num)
233: {
234: register u_int dig, i;
235: u_int ret = 0;
236:
237: if (num > 268435455)
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: */
259: inline u_int
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;
279: return ret;
280: }
281:
282: /*
283: * mqtt_sizeLen Return sizeof len field
284: *
285: * @len = length
286: * return: -1 error, >-1 sizeof len in bytes
287: */
288: inline char
289: mqtt_sizeLen(u_int len)
290: {
291: register char i;
292: u_char *p = (u_char*) &len;
293:
294: if (len > 0xffffff7f)
295: return -1;
296:
297: for (i = 0; i < sizeof len; i++)
298: if (!(*(p + i) & 0x80))
299: break;
300:
301: return ++i;
302: }
303:
304: /*
1.1.1.1.2.7 misho 305: * mqtt_str2subs Create MQTT subscribe variable from string(s)
1.1 misho 306: *
1.1.1.1.2.7 misho 307: * @csStr = null terminated string array
308: * @strnum = copy at most number of strings elements
1.1 misho 309: * @qoses = QoS elements applied to subscribe variable,
310: * count of elements must be equal with csStr elements
311: * return: NULL error or != subscribe variables array, must be free after use with mqtt_freeSub()
312: */
313: inline mqtt_subscr_t *
1.1.1.1.2.7 misho 314: mqtt_str2subs(const char **csStr, u_short strnum, u_char *qoses)
1.1 misho 315: {
316: mqtt_subscr_t *v;
317: register int i, items;
318: const char **strs;
319:
320: if (!csStr)
321: return NULL;
1.1.1.1.2.8 misho 322:
323: for (items = 0, strs = csStr;
324: (!strnum || (strnum && items < strnum)) && *strs;
325: items++, strs++);
1.1 misho 326:
327: if (!(v = malloc((items + 1) * sizeof(mqtt_subscr_t)))) {
328: LOGERR;
329: return NULL;
330: } else
331: memset(v, 0, (items + 1) * sizeof(mqtt_subscr_t));
332:
333: for (i = 0; i < items; i++) {
334: v[i].sub_topic.msg_len = strlen(csStr[i]);
335: v[i].sub_topic.msg_base = (u_char*) strdup(csStr[i]);
336: if (qoses && qoses[i] < MQTT_QOS_RESERVED)
337: v[i].sub_ret = qoses[i];
338: }
339:
340: return v;
341: }
342:
343: /*
344: * mqtt_subFree() Free array from subscribe variables
345: *
346: * @subs = Subscribe variables
347: * return: none
348: */
349: inline void
350: mqtt_subFree(mqtt_subscr_t ** __restrict subs)
351: {
352: mqtt_subscr_t *v;
353:
354: if (!subs)
355: return;
356:
357: for (v = *subs; v->sub_topic.msg_base; v++) {
358: free(v->sub_topic.msg_base);
359: v->sub_topic.msg_base = NULL;
360: v->sub_topic.msg_len = 0;
361:
362: if (v->sub_value.msg_base) {
363: free(v->sub_value.msg_base);
364: v->sub_value.msg_base = NULL;
365: v->sub_value.msg_len = 0;
366: }
367: }
368:
369: free(*subs);
370: *subs = NULL;
371: }
372:
373: /*
374: * mqtt_subAlloc() Create array from subscribe variables
375: *
376: * @num = Number of elements
377: * return: NULL error or subscribe array, after use must call mqtt_subFree()
378: */
379: inline mqtt_subscr_t *
380: mqtt_subAlloc(u_short num)
381: {
382: mqtt_subscr_t *s = NULL;
383:
384: s = malloc((num + 1) * sizeof(mqtt_subscr_t));
385: if (!s) {
386: LOGERR;
387: return NULL;
388: } else
389: memset(s, 0, (num + 1) * sizeof(mqtt_subscr_t));
390:
391: return s;
392: }
393:
394: /*
395: * mqtt_subRealloc() Reallocate array from subscribe variables
396: *
397: * @subs = Subscribe array
398: * @num = Number of elements
399: * return: NULL error or subscribe array, after use must call mqtt_subFree()
400: */
401: inline mqtt_subscr_t *
1.1.1.1.2.9 misho 402: mqtt_subRealloc(mqtt_subscr_t ** __restrict subs, u_short num)
1.1 misho 403: {
404: mqtt_subscr_t *s = NULL;
405:
1.1.1.1.2.9 misho 406: if (!subs)
407: return NULL;
408:
409: s = realloc(*subs, (num + 1) * sizeof(mqtt_subscr_t));
1.1 misho 410: if (!s) {
411: LOGERR;
412: return NULL;
1.1.1.1.2.9 misho 413: } else {
414: memset(s + num, 0, sizeof(mqtt_subscr_t));
415: *subs = s;
1.1 misho 416: }
417:
1.1.1.1.2.9 misho 418: return *subs;
1.1 misho 419: }
1.1.1.1.2.1 misho 420:
421: /*
1.1.1.1.2.10 misho 422: * mqtt_subCopy() - Copy subscription structure to another one
423: *
424: * @dst = destination subscription
425: * @src = source subscription
426: * return: =NULL error or !=NULL successful copied a structure
427: */
428: inline mqtt_subscr_t *
429: mqtt_subCopy(mqtt_subscr_t * __restrict dst, mqtt_subscr_t * __restrict src)
430: {
431: if (!dst || !src)
432: return NULL;
433:
434: if (src->sub_topic.msg_base) {
1.1.1.1.2.11 misho 435: dst->sub_topic.msg_base = malloc(src->sub_topic.msg_len + 1);
1.1.1.1.2.10 misho 436: if (!dst->sub_topic.msg_base) {
437: LOGERR;
438: memset(dst, 0, sizeof(mqtt_subscr_t));
439: return NULL;
440: } else {
441: dst->sub_topic.msg_len = src->sub_topic.msg_len;
1.1.1.1.2.11 misho 442: ((char*) dst->sub_topic.msg_base)[dst->sub_topic.msg_len] = 0;
1.1.1.1.2.10 misho 443: memcpy(dst->sub_topic.msg_base, src->sub_topic.msg_base,
444: dst->sub_topic.msg_len);
445: }
1.1.1.1.2.12 misho 446: } else {
1.1.1.1.2.13 misho 447: /*
1.1.1.1.2.12 misho 448: if (dst->sub_topic.msg_base)
449: free(dst->sub_topic.msg_base);
1.1.1.1.2.13 misho 450: */
1.1.1.1.2.12 misho 451: dst->sub_topic.msg_base = NULL;
452: dst->sub_topic.msg_len = 0;
1.1.1.1.2.10 misho 453: }
454: if (src->sub_value.msg_base) {
1.1.1.1.2.11 misho 455: dst->sub_value.msg_base = malloc(src->sub_value.msg_len + 1);
1.1.1.1.2.10 misho 456: if (!dst->sub_value.msg_base) {
457: LOGERR;
458: if (dst->sub_topic.msg_base)
459: free(dst->sub_topic.msg_base);
460: memset(dst, 0, sizeof(mqtt_subscr_t));
461: return NULL;
462: } else {
463: dst->sub_value.msg_len = src->sub_value.msg_len;
1.1.1.1.2.11 misho 464: ((char*) dst->sub_value.msg_base)[dst->sub_value.msg_len] = 0;
1.1.1.1.2.10 misho 465: memcpy(dst->sub_value.msg_base, src->sub_value.msg_base,
466: dst->sub_value.msg_len);
467: }
1.1.1.1.2.12 misho 468: } else {
1.1.1.1.2.13 misho 469: /*
1.1.1.1.2.12 misho 470: if (dst->sub_value.msg_base)
471: free(dst->sub_value.msg_base);
1.1.1.1.2.13 misho 472: */
1.1.1.1.2.12 misho 473: dst->sub_value.msg_base = NULL;
474: dst->sub_value.msg_len = 0;
1.1.1.1.2.10 misho 475: }
476:
477: dst->sub_ret = src->sub_ret;
478: return dst;
479: }
480:
481:
482: /*
1.1.1.1.2.1 misho 483: * mqtt_expandTopic() - Expanding topic to regular expression
484: *
485: * @csInput = Input topic
486: * @psRegEx = Output to regular expression
487: * @regexLen = Length of psRegEx
488: * @BOL = Begin of Line, if =0 not added
489: * @EOL = End of Line, if =0 not appended
490: * return: -1 error, 0 nothing expanded or >0 expanded bytes
491: */
492: int
493: mqtt_expandTopic(const char *csInput, char * __restrict psRegEx, int regexLen, u_char BOL, u_char EOL)
494: {
495: int ret = 0;
496: register int i;
497: char *pos, *s;
498: const char reROM[] = "[](){}^$\\-|?.+*";
499:
500: if (!csInput || !psRegEx || regexLen < 1)
501: return -1;
502: else
503: memset(psRegEx, 0, regexLen);
504:
505: /* check # */
506: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
507: if (*pos == '#')
508: i++;
509: if (i == 2) {
510: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
511: return -1;
512: }
513: if (i == 1 && (pos = strrchr(csInput, '#')))
514: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
515: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
516: return -1;
517: }
518: /* check + */
519: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
520: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
521: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
522: return -1;
523: }
524:
525: /* BUILD REGEX */
526: s = psRegEx;
527: if (BOL) {
528: *s++ = '^';
529: ret++;
530: }
531: for (pos = (char*) csInput; s < psRegEx + regexLen && *pos; s++, pos++) {
532: if (*pos == '#') {
533: strlcat(s, ".*", regexLen - (s - psRegEx));
534: s++;
535: ret++;
536: break;
537: }
538: if (*pos == '+') {
539: if (*(pos + 1)) {
540: strlcat(s, ".*", regexLen - (s - psRegEx));
541: s++;
542: ret++;
543: continue;
544: } else {
545: strlcat(s, ".*/", regexLen - (s - psRegEx));
546: ret += 2;
547: break;
548: }
549: }
550: for (i = 0; i < sizeof reROM - 1; i++)
551: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
552: *s++ = '\\';
553: ret++;
554: break;
555: }
556:
557: *s = *pos;
558: }
559: if (EOL) {
560: strlcat(psRegEx, "$", regexLen);
561: ret++;
562: }
563:
564: return ret;
565: }
1.1.1.1.2.2 misho 566:
567: /*
568: * mqtt_sqlTopic() - Expanding topic to SQL search string
569: *
570: * @csInput = Input topic
571: * @psSQL = Output to SQL search string
572: * @sqlLen = Length of psSQL
573: * return: -1 error, 0 changed bytes
574: */
575: int
576: mqtt_sqlTopic(const char *csInput, char * __restrict psSQL, int sqlLen)
577: {
578: int ret = 0;
579: register int i;
580: char *pos, *s;
581:
582: if (!csInput || !psSQL || sqlLen < 1)
583: return -1;
584: else
585: memset(psSQL, 0, sqlLen);
586:
587: /* check # */
588: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
589: if (*pos == '#')
590: i++;
591: if (i == 2) {
592: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
593: return -1;
594: }
595: if (i == 1 && (pos = strrchr(csInput, '#')))
596: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
597: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
598: return -1;
599: }
600: /* check + */
601: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
602: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
603: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
604: return -1;
605: }
606:
607: /* BUILD SEARCH STRING */
608: s = psSQL;
609: for (pos = (char*) csInput; s < psSQL + sqlLen && *pos; s++, pos++) {
610: if (*pos == '#') {
611: *s = '%';
612: s++;
613: ret++;
614: break;
615: }
616: if (*pos == '+') {
617: if (*(pos + 1)) {
618: *s = '%';
619: ret++;
620: continue;
621: } else {
622: strlcat(s, "%/", sqlLen - (s - psSQL));
623: ret += 2;
624: break;
625: }
626: }
627: /*
628: for (i = 0; i < sizeof reROM - 1; i++)
629: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
630: *s++ = '\\';
631: ret++;
632: break;
633: }
634: */
635:
636: *s = *pos;
637: }
638:
639: return ret;
640: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>