Annotation of libaitmqtt/src/conn.c, revision 1.2.2.1
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.2.2.1 ! misho 6: * $Id: conn.c,v 1.2 2012/06/20 15:02: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.2 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: /*
50: * mqtt_msgCONNECT() Create CONNECT message
51: *
52: * @buf = Message buffer
53: * @csConnID = ConnectID
1.2 misho 54: * @kasec = Keep alive timeout, if =0 default timeout for MQTT
1.1 misho 55: * @csUser = Username if !=NULL
56: * @csPass = Password for Username, only if csUser is set
57: * @csWillTopic = Will Topic if !=NULL Will Flags set into message
58: * @csWillMessage = Will Message, may be NULL
59: * @ClrSess = Clear Session subscriptions after disconnect
60: * @WillQOS = Will QOS if csWillTopic is set
61: * @WillRetain = Will Retain Will Message if csWillTopic is set
62: * return: -1 error or >-1 message size for send
63: */
64: int
65: mqtt_msgCONNECT(mqtt_msg_t * __restrict buf, const char *csConnID,
66: u_short kasec, const char *csUser, const char *csPass,
67: const char *csWillTopic, const char *csWillMessage,
68: u_char ClrSess, u_char WillQOS, u_char WillRetain)
69: {
1.2 misho 70: int len, siz = 0;
1.2.2.1 ! misho 71: u_int n, *l;
1.1 misho 72: struct mqtthdr *hdr;
73: mqtthdr_var_t *var, *cid, *topic, *wmsg, *user, *pass;
74: mqtthdr_protover_t *proto;
75: mqtthdr_connflgs_t *flags;
76: mqtt_len_t *ka;
1.2 misho 77: void *data;
1.1 misho 78:
79: if (!buf || !csConnID)
80: return -1;
81: if (strlen(csConnID) > 23) {
1.2 misho 82: mqtt_SetErr(EINVAL, "Invalid argument ConnID is too long (max 23 bytes)");
1.1 misho 83: return -1;
84: }
85: if (csUser && strlen(csUser) > 12) {
1.2 misho 86: mqtt_SetErr(EINVAL, "Invalid argument Username is too long (max 12 bytes)");
1.1 misho 87: return -1;
88: }
89: if (csPass && strlen(csPass) > 12) {
1.2 misho 90: mqtt_SetErr(EINVAL, "Invalid argument Password is too long (max 12 bytes)");
1.1 misho 91: return -1;
92: }
93: if (WillQOS > MQTT_QOS_EXACTLY) {
1.2 misho 94: mqtt_SetErr(EINVAL, "Invalid argument WillQOS - unknown QOS value");
1.1 misho 95: return -1;
96: }
97:
1.2 misho 98: /* calculate message size */
99: len = 10 + sizeof(mqtt_len_t); /* connect arguments */
100: len += sizeof(mqtt_len_t) + strlen(csConnID); /* connect id */
101: if (csUser && *csUser) { /* user/pass */
102: len += sizeof(mqtt_len_t) + strlen(csUser);
103: if (csPass)
104: len += sizeof(mqtt_len_t) + strlen(csPass);
105: }
106: if (csWillTopic && *csWillTopic) { /* will messages */
107: len += sizeof(mqtt_len_t) + strlen(csWillTopic);
108: len += sizeof(mqtt_len_t) + (csWillMessage ? strlen(csWillMessage) : 0);
109: }
110:
111: /* calculate header size */
112: siz = sizeof(struct mqtthdr); /* mqtt fixed header */
113: n = mqtt_encodeLen(len); /* message size */
114: siz += mqtt_sizeLen(n) - 1; /* length size */
115:
116: if (mqtt_msgRealloc(buf, siz + len) == -1)
1.1 misho 117: return -1;
118: else {
1.2 misho 119: data = buf->msg_base;
120: hdr = (struct mqtthdr *) data;
1.1 misho 121: }
122:
123: /* fixed header */
124: MQTTHDR_MSGINIT(hdr);
125: hdr->mqtt_msg.type = MQTT_TYPE_CONNECT;
1.2.2.1 ! misho 126: l = (u_int*) hdr->mqtt_len;
! 127: *l = n;
1.2 misho 128: data += siz;
1.1 misho 129:
130: /* variable header */
1.2 misho 131: var = (mqtthdr_var_t*) data;
132: var->var_sb.val = htons(strlen(MQTT_CONN_STR));
133: memcpy(var->var_data, MQTT_CONN_STR, ntohs(var->var_sb.val));
134: data += MQTTHDR_VAR_SIZEOF(var);
1.1 misho 135:
1.2 misho 136: /* protocol version */
137: proto = (mqtthdr_protover_t*) data++;
1.1 misho 138: *proto = MQTT_PROTO_VER;
139:
140: /* CONNECT header */
1.2 misho 141: flags = (mqtthdr_connflgs_t*) data++;
1.1 misho 142: flags->clean_sess = ClrSess ? 1 : 0;
143: if (csUser && *csUser) {
144: flags->username = 1;
145: flags->password = csPass ? 1 : 0;
146: } else {
147: flags->username = 0;
148: flags->password = 0;
149: }
150: if (csWillTopic && *csWillTopic) {
151: flags->will_flg = 1;
152: flags->will_qos = WillQOS;
153: flags->will_retain = WillRetain ? 1 : 0;
154: } else {
155: flags->will_flg = 0;
156: flags->will_qos = 0;
157: flags->will_retain = 0;
158: }
159:
1.2 misho 160: /* keep alive */
161: ka = (mqtt_len_t*) data;
1.1 misho 162: ka->val = kasec ? htons(kasec) : htons(MQTT_KEEPALIVE);
1.2 misho 163: data += sizeof(mqtt_len_t);
1.1 misho 164:
165: /* ConnID */
1.2 misho 166: cid = (mqtthdr_var_t*) data;
1.1 misho 167: cid->var_sb.val = htons(strlen(csConnID));
168: memcpy(cid->var_data, csConnID, ntohs(cid->var_sb.val));
1.2 misho 169: data += MQTTHDR_VAR_SIZEOF(cid);
1.1 misho 170:
171: /* If Will Flags setup */
172: if (csWillTopic && *csWillTopic) {
1.2 misho 173: topic = (mqtthdr_var_t*) data;
1.1 misho 174: topic->var_sb.val = htons(strlen(csWillTopic));
175: memcpy(topic->var_data, csWillTopic, ntohs(topic->var_sb.val));
1.2 misho 176: data += MQTTHDR_VAR_SIZEOF(topic);
1.1 misho 177:
1.2 misho 178: wmsg = (mqtthdr_var_t*) data;
1.1 misho 179: if (csWillMessage && *csWillMessage) {
180: wmsg->var_sb.val = htons(strlen(csWillMessage));
181: memcpy(wmsg->var_data, csWillMessage, ntohs(wmsg->var_sb.val));
182: } else
183: wmsg->var_sb.val = 0;
1.2 misho 184: data += MQTTHDR_VAR_SIZEOF(wmsg);
1.1 misho 185: }
186:
187: /* If defined Username & Password */
188: if (csUser && *csUser) {
1.2 misho 189: user = (mqtthdr_var_t*) data;
1.1 misho 190: user->var_sb.val = htons(strlen(csUser));
191: memcpy(user->var_data, csUser, ntohs(user->var_sb.val));
1.2 misho 192: data += MQTTHDR_VAR_SIZEOF(user);
1.1 misho 193:
194: if (csPass && *csPass) {
1.2 misho 195: pass = (mqtthdr_var_t*) data;
1.1 misho 196: pass->var_sb.val = htons(strlen(csPass));
197: memcpy(pass->var_data, csPass, ntohs(pass->var_sb.val));
1.2 misho 198: data += MQTTHDR_VAR_SIZEOF(pass);
1.1 misho 199: }
200: }
201:
1.2 misho 202: return siz + len;
1.1 misho 203: }
204:
205: /*
206: * mqtt_msgCONNACK() Create CONNACK message
207: *
208: * @buf = Message buffer
209: * @retcode = Return code
210: * return: -1 error or >-1 message size for send
211: */
212: int
213: mqtt_msgCONNACK(mqtt_msg_t * __restrict buf, u_char retcode)
214: {
215: int siz = 0;
216: struct mqtthdr *hdr;
217: mqtthdr_connack_t *ack;
218:
219: if (!buf)
220: return -1;
221: if (retcode > MQTT_RETCODE_DENIED) {
1.2 misho 222: mqtt_SetErr(EINVAL, "Invalid retcode");
1.1 misho 223: return -1;
224: }
225:
226: if (mqtt_msgRealloc(buf, sizeof(struct mqtthdr) + sizeof(mqtthdr_connack_t)) == -1)
227: return -1;
228: else {
229: hdr = (struct mqtthdr *) (buf->msg_base + siz);
230: siz += sizeof(struct mqtthdr);
231: ack = (mqtthdr_connack_t*) (buf->msg_base + siz);
232: siz += sizeof(mqtthdr_connack_t);
233: }
234:
235: /* fixed header */
236: MQTTHDR_MSGINIT(hdr);
237: hdr->mqtt_msg.type = MQTT_TYPE_CONNACK;
238: *hdr->mqtt_len = sizeof(mqtthdr_connack_t);
239:
240: /* CONNACK header */
241: ack->reserved = 0;
242: ack->retcode = retcode;
243:
244: return siz;
245: }
246:
247: static int
248: _mqtt_msgSIMPLE_(mqtt_msg_t * __restrict buf, u_char cmd)
249: {
250: int siz = 0;
251: struct mqtthdr *hdr;
252:
253: if (!buf)
254: return -1;
255:
256: if (mqtt_msgRealloc(buf, sizeof(struct mqtthdr)) == -1)
257: return -1;
258: else {
259: hdr = (struct mqtthdr *) (buf->msg_base + siz);
260: siz += sizeof(struct mqtthdr);
261: }
262:
263: /* fixed header */
264: MQTTHDR_MSGINIT(hdr);
265: hdr->mqtt_msg.type = cmd;
266: *hdr->mqtt_len = 0;
267:
268: return siz;
269: }
270:
271: /*
272: * mqtt_msgPINGREQ() Create PINGREQ message
273: *
274: * @buf = Message buffer
275: * return: -1 error or >-1 message size for send
276: */
277: int
278: mqtt_msgPINGREQ(mqtt_msg_t * __restrict buf)
279: {
280: return _mqtt_msgSIMPLE_(buf, MQTT_TYPE_PINGREQ);
281: }
282:
283: /*
284: * mqtt_msgPINGRESP() Create PINGRESP message
285: *
286: * @buf = Message buffer
287: * return: -1 error or >-1 message size for send
288: */
289: int
290: mqtt_msgPINGRESP(mqtt_msg_t * __restrict buf)
291: {
292: return _mqtt_msgSIMPLE_(buf, MQTT_TYPE_PINGRESP);
293: }
294:
295: /*
296: * mqtt_msgDISCONNECT() Create DISCONNECT message
297: *
298: * @buf = Message buffer
299: * return: -1 error or >-1 message size for send
300: */
301: int
302: mqtt_msgDISCONNECT(mqtt_msg_t * __restrict buf)
303: {
304: return _mqtt_msgSIMPLE_(buf, MQTT_TYPE_DISCONNECT);
305: }
306:
307: /* ============= decode ============ */
308:
309: /*
310: * mqtt_readCONNECT() Read elements from CONNECT message
311: *
312: * @buf = Message buffer
313: * @kasec = Keep Alive in seconds for current connection
314: * @psConnID = ConnectID
315: * @connLen = ConnectID length
316: * @psUser = Username if !=NULL
317: * @userLen = Username length
318: * @psPass = Password for Username, only if csUser is set
319: * @passLen = Password length
320: * @psWillTopic = Will Topic if !=NULL Will Flags set into message and must be free()
321: * @psWillMessage = Will Message, may be NULL if !NULL must be free() after use!
322: * return: .reserved == 1 is error or == 0 connection flags & msg ok
323: */
324: mqtthdr_connack_t
325: mqtt_readCONNECT(mqtt_msg_t * __restrict buf, u_short *kasec, char * __restrict psConnID, int connLen,
326: char * __restrict psUser, int userLen, char * __restrict psPass, int passLen,
327: char ** __restrict psWillTopic, char ** __restrict psWillMessage)
328: {
329: mqtthdr_connflgs_t flg = { MQTT_CONNFLGS_INIT };
330: mqtthdr_connack_t cack = { 1, MQTT_RETCODE_DENIED };
331: struct mqtthdr *hdr;
332: mqtthdr_var_t *var;
333: mqtt_len_t *ka;
334: mqtthdr_protover_t *proto;
335: int len, ret;
336: caddr_t pos;
337:
338: if (!buf || !buf->msg_base || !buf->msg_len || !psConnID || !connLen)
339: return cack;
340:
341: hdr = _mqtt_readHEADER(buf, MQTT_TYPE_CONNECT, &ret, &len);
342: if (!hdr)
343: return cack;
344: if (len < 12) {
1.2 misho 345: mqtt_SetErr(EINVAL, "Short message length %d", len);
1.1 misho 346: return cack;
347: } else {
348: pos = buf->msg_base + ret + 1;
349: var = (mqtthdr_var_t*) pos;
350: }
351: /* check init string & protocol */
352: if (var->var_sb.sb.l != 6 || strncmp((char*) var->var_data, MQTT_CONN_STR, 6)) {
1.2 misho 353: mqtt_SetErr(EINVAL, "Invalid init string %.6s(%d)",
1.1 misho 354: var->var_data, var->var_sb.sb.l);
355: cack.retcode = MQTT_RETCODE_REFUSE_UNAVAIL;
356: return cack;
357: } else {
358: pos += var->var_sb.sb.l + sizeof(mqtt_len_t);
359: proto = (mqtthdr_protover_t*) pos;
360: }
361: if (*proto != MQTT_PROTO_VER) {
1.2 misho 362: mqtt_SetErr(EINVAL, "Invalid protocol version %d", *pos);
1.1 misho 363: cack.retcode = MQTT_RETCODE_REFUSE_VER;
364: return cack;
365: } else
366: pos++;
367: flg = *(mqtthdr_connflgs_t*) pos;
368: pos++;
369: ka = (mqtt_len_t*) pos;
370: *kasec = ntohs(ka->val);
371: pos += sizeof(mqtt_len_t);
372:
373: len -= pos - (caddr_t) var;
374:
375: /* get ConnID */
376: var = (mqtthdr_var_t*) pos;
377: len -= MQTTHDR_VAR_SIZEOF(var);
378: if (len < 0 || var->var_sb.sb.l > 23) {
1.2 misho 379: mqtt_SetErr(EINVAL, "Unexpected EOM at Connection ID %d", len);
1.1 misho 380: cack.retcode = MQTT_RETCODE_REFUSE_ID;
381: return cack;
382: } else {
383: memset(psConnID, 0, connLen--);
384: memcpy(psConnID, var->var_data, ntohs(var->var_sb.val) > connLen ? connLen : ntohs(var->var_sb.val));
385: pos += MQTTHDR_VAR_SIZEOF(var);
386: }
387:
388: /* get Willz */
389: if (flg.will_flg) {
390: var = (mqtthdr_var_t*) pos;
391: len -= MQTTHDR_VAR_SIZEOF(var);
392: if (len < 0) {
1.2 misho 393: mqtt_SetErr(EINVAL, "Unexpected EOM at Will Topic %d", len);
1.1 misho 394: cack.retcode = MQTT_RETCODE_REFUSE_ID;
395: return cack;
396: } else {
397: if (psWillTopic) {
398: *psWillTopic = malloc(ntohs(var->var_sb.val) + 1);
399: if (!*psWillTopic) {
400: LOGERR;
401: cack.retcode = MQTT_RETCODE_REFUSE_UNAVAIL;
402: return cack;
403: } else
404: memset(*psWillTopic, 0, ntohs(var->var_sb.val) + 1);
405: memcpy(*psWillTopic, var->var_data, ntohs(var->var_sb.val));
406: }
407: pos += MQTTHDR_VAR_SIZEOF(var);
408: }
409:
410: var = (mqtthdr_var_t*) pos;
411: len -= MQTTHDR_VAR_SIZEOF(var);
412: if (len < 0) {
1.2 misho 413: mqtt_SetErr(EINVAL, "Unexpected EOM at Will Message %d", len);
1.1 misho 414: cack.retcode = MQTT_RETCODE_REFUSE_ID;
415: return cack;
416: } else {
417: if (psWillMessage) {
418: *psWillMessage = malloc(ntohs(var->var_sb.val) + 1);
419: if (!*psWillMessage) {
420: LOGERR;
421: cack.retcode = MQTT_RETCODE_REFUSE_UNAVAIL;
422: return cack;
423: } else
424: memset(*psWillMessage, 0, ntohs(var->var_sb.val) + 1);
425: memcpy(*psWillMessage, var->var_data, ntohs(var->var_sb.val));
426: }
427: pos += MQTTHDR_VAR_SIZEOF(var);
428: }
429: }
430:
431: /* get User/Pass */
432: if (flg.username) {
433: var = (mqtthdr_var_t*) pos;
434: len -= MQTTHDR_VAR_SIZEOF(var);
435: if (len < 0 || var->var_sb.sb.l > 12) {
1.2 misho 436: mqtt_SetErr(EINVAL, "Unexpected EOM at Username %d", len);
1.1 misho 437: cack.retcode = MQTT_RETCODE_REFUSE_USERPASS;
438: return cack;
439: } else {
440: if (psUser && userLen) {
441: memset(psUser, 0, userLen--);
442: memcpy(psUser, var->var_data,
443: ntohs(var->var_sb.val) > userLen ? userLen : ntohs(var->var_sb.val));
444: }
445: pos += MQTTHDR_VAR_SIZEOF(var);
446: }
447: }
448: if (flg.password) {
449: var = (mqtthdr_var_t*) pos;
450: len -= MQTTHDR_VAR_SIZEOF(var);
451: if (len < 0 || var->var_sb.sb.l > 12) {
1.2 misho 452: mqtt_SetErr(EINVAL, "Unexpected EOM at Password %d", len);
1.1 misho 453: cack.retcode = MQTT_RETCODE_REFUSE_USERPASS;
454: return cack;
455: } else {
456: if (psPass && passLen) {
457: memset(psPass, 0, passLen--);
458: memcpy(psPass, var->var_data,
459: ntohs(var->var_sb.val) > passLen ? passLen : ntohs(var->var_sb.val));
460: }
461: pos += MQTTHDR_VAR_SIZEOF(var);
462: }
463: }
464:
465: flg.reserved = 0;
466: cack.reserved = flg.flags;
467: cack.retcode = MQTT_RETCODE_ACCEPTED;
468: return cack;
469: }
470:
471: /*
472: * mqtt_readCONNACK() Read CONNACK message
473: *
474: * @buf = Message buffer
475: * return: -1 error or >-1 CONNECT message return code
476: */
477: u_char
478: mqtt_readCONNACK(mqtt_msg_t * __restrict buf)
479: {
480: int len, ret;
481: struct mqtthdr *hdr;
482: mqtthdr_connack_t *ack;
483: caddr_t pos;
484:
485: if (!buf || !buf->msg_base || !buf->msg_len)
486: return (u_char) -1;
487:
488: hdr = _mqtt_readHEADER(buf, MQTT_TYPE_CONNACK, &ret, &len);
489: if (!hdr)
490: return (u_char) -1;
491: if (len < sizeof(mqtthdr_connack_t)) {
1.2 misho 492: mqtt_SetErr(EINVAL, "Short message length %d", len);
1.1 misho 493: return (u_char) -1;
494: } else {
495: pos = buf->msg_base + ret + 1;
496: ack = (mqtthdr_connack_t*) pos;
497: }
498:
499: if (ack->retcode > MQTT_RETCODE_DENIED) {
1.2 misho 500: mqtt_SetErr(EINVAL, "Invalid retcode %u", ack->retcode);
1.1 misho 501: return (u_char) -1;
502: }
503:
504: return ack->retcode;
505: }
506:
507: /*
508: * mqtt_readDISCONNECT() Read DISCONNECT message
509: *
510: * @buf = Message buffer
511: * return: -1 error, 0 ok, >0 undefined result
512: */
513: int
514: mqtt_readDISCONNECT(mqtt_msg_t * __restrict buf)
515: {
516: int len, ret;
517: struct mqtthdr *hdr;
518:
519: hdr = _mqtt_readHEADER(buf, MQTT_TYPE_DISCONNECT, &ret, &len);
520: if (!hdr || ret != 1)
521: return -1;
522:
523: return len;
524: }
525:
526: /*
527: * mqtt_readPINGREQ() Read PINGREQ message
528: *
529: * @buf = Message buffer
530: * return: -1 error, 0 ok, >0 undefined result
531: */
532: int
533: mqtt_readPINGREQ(mqtt_msg_t * __restrict buf)
534: {
535: int len, ret;
536: struct mqtthdr *hdr;
537:
538: hdr = _mqtt_readHEADER(buf, MQTT_TYPE_PINGREQ, &ret, &len);
539: if (!hdr || ret != 1)
540: return -1;
541:
542: return len;
543: }
544:
545: /*
546: * mqtt_readPINGRESP() Read PINGRESP message
547: *
548: * @buf = Message buffer
549: * return: -1 error, 0 ok, >0 undefined result
550: */
551: int
552: mqtt_readPINGRESP(mqtt_msg_t * __restrict buf)
553: {
554: int len, ret;
555: struct mqtthdr *hdr;
556:
557: hdr = _mqtt_readHEADER(buf, MQTT_TYPE_PINGRESP, &ret, &len);
558: if (!hdr || ret != 1)
559: return -1;
560:
561: return len;
562: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>