Annotation of libaitmqtt/src/aitmqtt.c, revision 1.3
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 ! misho 6: * $Id: aitmqtt.c,v 1.2.10.1 2013/05/26 20:27:58 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 ! misho 15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
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.1 misho 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: */
1.3 ! misho 168: int
1.1 misho 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.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:
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: */
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;
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: */
1.3 ! misho 288: char
1.1 misho 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.2 misho 305: * mqtt_pktLen() - Get total packet length
306: *
307: * @hdr = MQTT packet header
308: * return: packet length
309: */
1.3 ! misho 310: u_int
1.2 misho 311: mqtt_pktLen(struct mqtthdr * __restrict hdr)
312: {
313: int siz, n = 0;
314:
315: if (!hdr)
316: return 0;
317:
318: siz = mqtt_decodeLen(hdr->mqtt_len, &n);
319: siz += sizeof(struct mqtthdr) + n - 1;
320:
321: return siz;
322: }
323:
324: /*
325: * mqtt_str2subs Create MQTT subscribe variable from string(s)
1.1 misho 326: *
1.2 misho 327: * @csStr = null terminated string array
328: * @strnum = copy at most number of strings elements
1.1 misho 329: * @qoses = QoS elements applied to subscribe variable,
330: * count of elements must be equal with csStr elements
331: * return: NULL error or != subscribe variables array, must be free after use with mqtt_freeSub()
332: */
1.3 ! misho 333: mqtt_subscr_t *
1.2 misho 334: mqtt_str2subs(const char **csStr, u_short strnum, u_char *qoses)
1.1 misho 335: {
336: mqtt_subscr_t *v;
337: register int i, items;
338: const char **strs;
339:
340: if (!csStr)
341: return NULL;
1.2 misho 342:
343: for (items = 0, strs = csStr;
344: (!strnum || (strnum && items < strnum)) && *strs;
345: items++, strs++);
1.1 misho 346:
347: if (!(v = malloc((items + 1) * sizeof(mqtt_subscr_t)))) {
348: LOGERR;
349: return NULL;
350: } else
351: memset(v, 0, (items + 1) * sizeof(mqtt_subscr_t));
352:
353: for (i = 0; i < items; i++) {
354: v[i].sub_topic.msg_len = strlen(csStr[i]);
355: v[i].sub_topic.msg_base = (u_char*) strdup(csStr[i]);
356: if (qoses && qoses[i] < MQTT_QOS_RESERVED)
357: v[i].sub_ret = qoses[i];
358: }
359:
360: return v;
361: }
362:
363: /*
364: * mqtt_subFree() Free array from subscribe variables
365: *
366: * @subs = Subscribe variables
367: * return: none
368: */
1.3 ! misho 369: void
1.1 misho 370: mqtt_subFree(mqtt_subscr_t ** __restrict subs)
371: {
372: mqtt_subscr_t *v;
373:
374: if (!subs)
375: return;
376:
377: for (v = *subs; v->sub_topic.msg_base; v++) {
378: free(v->sub_topic.msg_base);
379: v->sub_topic.msg_base = NULL;
380: v->sub_topic.msg_len = 0;
381:
382: if (v->sub_value.msg_base) {
383: free(v->sub_value.msg_base);
384: v->sub_value.msg_base = NULL;
385: v->sub_value.msg_len = 0;
386: }
387: }
388:
389: free(*subs);
390: *subs = NULL;
391: }
392:
393: /*
394: * mqtt_subAlloc() Create array from subscribe variables
395: *
396: * @num = Number of elements
397: * return: NULL error or subscribe array, after use must call mqtt_subFree()
398: */
1.3 ! misho 399: mqtt_subscr_t *
1.1 misho 400: mqtt_subAlloc(u_short num)
401: {
402: mqtt_subscr_t *s = NULL;
403:
404: s = malloc((num + 1) * sizeof(mqtt_subscr_t));
405: if (!s) {
406: LOGERR;
407: return NULL;
408: } else
409: memset(s, 0, (num + 1) * sizeof(mqtt_subscr_t));
410:
411: return s;
412: }
413:
414: /*
415: * mqtt_subRealloc() Reallocate array from subscribe variables
416: *
417: * @subs = Subscribe array
418: * @num = Number of elements
419: * return: NULL error or subscribe array, after use must call mqtt_subFree()
420: */
1.3 ! misho 421: mqtt_subscr_t *
1.2 misho 422: mqtt_subRealloc(mqtt_subscr_t ** __restrict subs, u_short num)
1.1 misho 423: {
424: mqtt_subscr_t *s = NULL;
425:
1.2 misho 426: if (!subs)
427: return NULL;
428:
429: s = realloc(*subs, (num + 1) * sizeof(mqtt_subscr_t));
1.1 misho 430: if (!s) {
431: LOGERR;
432: return NULL;
1.2 misho 433: } else {
434: memset(s + num, 0, sizeof(mqtt_subscr_t));
435: *subs = s;
436: }
437:
438: return *subs;
439: }
440:
441: /*
442: * mqtt_subCopy() - Copy subscription structure to another one
443: *
444: * @dst = destination subscription
445: * @src = source subscription
446: * return: =NULL error or !=NULL successful copied a structure
447: */
1.3 ! misho 448: mqtt_subscr_t *
1.2 misho 449: mqtt_subCopy(mqtt_subscr_t * __restrict dst, mqtt_subscr_t * __restrict src)
450: {
451: if (!dst || !src)
452: return NULL;
453:
454: if (src->sub_topic.msg_base) {
455: dst->sub_topic.msg_base = malloc(src->sub_topic.msg_len + 1);
456: if (!dst->sub_topic.msg_base) {
457: LOGERR;
458: memset(dst, 0, sizeof(mqtt_subscr_t));
459: return NULL;
460: } else {
461: dst->sub_topic.msg_len = src->sub_topic.msg_len;
462: ((char*) dst->sub_topic.msg_base)[dst->sub_topic.msg_len] = 0;
463: memcpy(dst->sub_topic.msg_base, src->sub_topic.msg_base,
464: dst->sub_topic.msg_len);
465: }
466: } else {
467: /*
468: if (dst->sub_topic.msg_base)
469: free(dst->sub_topic.msg_base);
470: */
471: dst->sub_topic.msg_base = NULL;
472: dst->sub_topic.msg_len = 0;
473: }
474: if (src->sub_value.msg_base) {
475: dst->sub_value.msg_base = malloc(src->sub_value.msg_len + 1);
476: if (!dst->sub_value.msg_base) {
477: LOGERR;
478: if (dst->sub_topic.msg_base)
479: free(dst->sub_topic.msg_base);
480: memset(dst, 0, sizeof(mqtt_subscr_t));
481: return NULL;
482: } else {
483: dst->sub_value.msg_len = src->sub_value.msg_len;
484: ((char*) dst->sub_value.msg_base)[dst->sub_value.msg_len] = 0;
485: memcpy(dst->sub_value.msg_base, src->sub_value.msg_base,
486: dst->sub_value.msg_len);
487: }
488: } else {
489: /*
490: if (dst->sub_value.msg_base)
491: free(dst->sub_value.msg_base);
492: */
493: dst->sub_value.msg_base = NULL;
494: dst->sub_value.msg_len = 0;
495: }
496:
497: dst->sub_ret = src->sub_ret;
498: return dst;
499: }
500:
501:
502: /*
503: * mqtt_expandTopic() - Expanding topic to regular expression
504: *
505: * @csInput = Input topic
506: * @psRegEx = Output to regular expression
507: * @regexLen = Length of psRegEx
508: * @BOL = Begin of Line, if =0 not added
509: * @EOL = End of Line, if =0 not appended
510: * return: -1 error, 0 nothing expanded or >0 expanded bytes
511: */
512: int
513: mqtt_expandTopic(const char *csInput, char * __restrict psRegEx, int regexLen, u_char BOL, u_char EOL)
514: {
515: int ret = 0;
516: register int i;
517: char *pos, *s;
518: const char reROM[] = "[](){}^$\\-|?.+*";
519:
520: if (!csInput || !psRegEx || regexLen < 1)
521: return -1;
522: else
523: memset(psRegEx, 0, regexLen);
524:
525: /* check # */
526: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
527: if (*pos == '#')
528: i++;
529: if (i == 2) {
530: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
531: return -1;
532: }
533: if (i == 1 && (pos = strrchr(csInput, '#')))
534: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
535: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
536: return -1;
537: }
538: /* check + */
539: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
540: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
541: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
542: return -1;
543: }
544:
545: /* BUILD REGEX */
546: s = psRegEx;
547: if (BOL) {
548: *s++ = '^';
549: ret++;
550: }
551: for (pos = (char*) csInput; s < psRegEx + regexLen && *pos; s++, pos++) {
552: if (*pos == '#') {
553: strlcat(s, ".*", regexLen - (s - psRegEx));
554: s++;
555: ret++;
556: break;
557: }
558: if (*pos == '+') {
559: if (*(pos + 1)) {
560: strlcat(s, ".*", regexLen - (s - psRegEx));
561: s++;
562: ret++;
563: continue;
564: } else {
565: strlcat(s, ".*/", regexLen - (s - psRegEx));
566: ret += 2;
567: break;
568: }
569: }
570: for (i = 0; i < sizeof reROM - 1; i++)
571: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
572: *s++ = '\\';
573: ret++;
574: break;
575: }
576:
577: *s = *pos;
1.1 misho 578: }
1.2 misho 579: if (EOL) {
580: strlcat(psRegEx, "$", regexLen);
581: ret++;
582: }
583:
584: return ret;
585: }
586:
587: /*
588: * mqtt_sqlTopic() - Expanding topic to SQL search string
589: *
590: * @csInput = Input topic
591: * @psSQL = Output to SQL search string
592: * @sqlLen = Length of psSQL
593: * return: -1 error, 0 changed bytes
594: */
595: int
596: mqtt_sqlTopic(const char *csInput, char * __restrict psSQL, int sqlLen)
597: {
598: int ret = 0;
599: register int i;
600: char *pos, *s;
601:
602: if (!csInput || !psSQL || sqlLen < 1)
603: return -1;
604: else
605: memset(psSQL, 0, sqlLen);
1.1 misho 606:
1.2 misho 607: /* check # */
608: for (i = 0, pos = (char*) csInput; *pos && i < 2; pos++)
609: if (*pos == '#')
610: i++;
611: if (i == 2) {
612: mqtt_SetErr(EINVAL, "Syntax error, multiple occurrences of #..#");
613: return -1;
614: }
615: if (i == 1 && (pos = strrchr(csInput, '#')))
616: if ((pos != csInput && *(pos - 1) != '/') || *(pos + 1)) {
617: mqtt_SetErr(EINVAL, "Syntax error, bad format of #");
618: return -1;
619: }
620: /* check + */
621: for (pos = (char*) csInput; *pos && (pos = strchr(pos, '+')); pos++)
622: if ((pos != csInput && *(pos - 1) != '/') || (*(pos + 1) && *(pos + 1) != '/')) {
623: mqtt_SetErr(EINVAL, "Syntax error, bad format of +");
624: return -1;
625: }
626:
627: /* BUILD SEARCH STRING */
628: s = psSQL;
629: for (pos = (char*) csInput; s < psSQL + sqlLen && *pos; s++, pos++) {
630: if (*pos == '#') {
631: *s = '%';
632: s++;
633: ret++;
634: break;
635: }
636: if (*pos == '+') {
637: if (*(pos + 1)) {
638: *s = '%';
639: ret++;
640: continue;
641: } else {
642: strlcat(s, "%/", sqlLen - (s - psSQL));
643: ret += 2;
644: break;
645: }
646: }
647: /*
648: for (i = 0; i < sizeof reROM - 1; i++)
649: if (*pos == reROM[i] && regexLen - (s - psRegEx) - 1 > 0) {
650: *s++ = '\\';
651: ret++;
652: break;
653: }
654: */
655:
656: *s = *pos;
657: }
658:
659: return ret;
1.1 misho 660: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>