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