/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2020 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * MQTT v5 * * http://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html */ #include "private-lib-core.h" /* #include "lws-mqtt.h" */ #include #include #include #include /* * Encode is done into a buffer of at least 4 bytes space. * * Returns -1 for error, or number of bytes used */ int lws_mqtt_vbi_encode(uint32_t value, void *buf) { uint8_t *p = (uint8_t *)buf, b; if (value > 0xfffffff) { assert(0); return -1; } do { b = value & 0x7f; value >>= 7; if (value) *p++ = (0x80 | b); else *p++ = b; } while (value); return lws_ptr_diff(p, (uint8_t *)buf); } void lws_mqtt_vbi_init(lws_mqtt_vbi *vbi) { vbi->value = 0; vbi->consumed = 0; vbi->budget = 4; } void lws_mqtt_2byte_init(lws_mqtt_vbi *vbi) { vbi->value = 0; vbi->consumed = 0; vbi->budget = 2; } void lws_mqtt_4byte_init(lws_mqtt_vbi *vbi) { vbi->value = 0; vbi->consumed = 0; vbi->budget = 4; } lws_mqtt_stateful_primitive_return_t lws_mqtt_vbi_r(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len) { uint8_t multiplier = 0; if (!vbi->budget) { lwsl_info("%s: bad vbi\n", __func__); return LMSPR_FAILED_ALREADY_COMPLETED; } while (*len && vbi->budget--) { uint8_t u = *((*in)++); (*len)--; vbi->consumed++; vbi->value += (u & 0x7f) << multiplier; multiplier += 7; if (!(u & 0x80)) return LMSPR_COMPLETED; /* finished */ } if (!vbi->budget) { /* should have ended on b7 = 0 and exited then... */ lwsl_info("%s: bad vbi\n", __func__); return LMSPR_FAILED_FORMAT; } return LMSPR_NEED_MORE; } lws_mqtt_stateful_primitive_return_t lws_mqtt_mb_parse(lws_mqtt_vbi *vbi, const uint8_t **in, size_t *len) { if (!vbi->budget) return LMSPR_FAILED_ALREADY_COMPLETED; while (*len && vbi->budget--) { vbi->value = (vbi->value << 8) | *((*in)++); (*len)--; vbi->consumed++; } return vbi->budget ? LMSPR_NEED_MORE : LMSPR_COMPLETED; } /* * You can leave buf NULL, if so it will be allocated on the heap once the * actual length is known. nf should be 0, it will be set at allocation time. * * Or you can ensure no allocation and use an external buffer by setting buf * and lim. But buf must be in the ep context somehow, since it may have to * survive returns to the event loop unchanged. Set nf to 0 in this case. * * Or you can set buf to an externally allocated buffer, in which case you may * set nf so it will be freed when the string is "freed". */ void lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf) { s->len = 0; /* at COMPLETED, consumed count is s->len + 2 */ s->pos = 0; s->buf = buf; s->limit = lim; s->len_valid = 0; s->needs_freeing = nf; } lws_mqtt_str_t * lws_mqtt_str_create(uint16_t lim) { lws_mqtt_str_t *s = lws_malloc(sizeof(*s) + lim + 1, __func__); if (!s) return NULL; s->len = 0; s->pos = 0; s->buf = (uint8_t *)&s[1]; s->limit = lim; s->len_valid = 0; s->needs_freeing = 1; return s; } lws_mqtt_str_t * lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim) { lws_mqtt_str_t *s; if (!lim) lim = len; s = lws_mqtt_str_create(lim); if (!s) return NULL; memcpy(s->buf, buf, len); s->len = len; s->len_valid = 1; s->pos = len; return s; } lws_mqtt_str_t * lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim) { size_t len = strlen(buf); if (!lim) lim = (uint16_t)len; return lws_mqtt_str_create_init((uint8_t *)buf, (uint16_t)len, lim); } uint8_t * lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget) { if (budget) *budget = s->limit - s->pos; return &s->buf[s->pos]; } int lws_mqtt_str_advance(lws_mqtt_str_t *s, int n) { if (n > s->limit - s->pos) { lwsl_err("%s: attempted overflow %d vs %d\n", __func__, n, s->limit - s->pos); return 1; } s->pos += n; s->len += n; return 0; } void lws_mqtt_str_free(lws_mqtt_str_t **ps) { lws_mqtt_str_t *s = *ps; if (!s || !s->needs_freeing) return; /* buf may be independently allocated or allocated along with the * lws_mqtt_str_t at the end... if so the whole lws_mqtt_str_t is freed. */ if (s->buf != (uint8_t *)&s[1]) lws_free_set_NULL(s->buf); else lws_free_set_NULL(*ps); } /* * Parses and allocates for lws_mqtt_str_t in a fragmentation-immune, but * efficient for bulk data way. * * Returns: LMSPR_NEED_MORE if needs more data, * LMSPR_COMPLETED if complete, <0 for error * * *len is reduced by, and *in is advanced by, the amount of data actually used, * except in error case * * lws_mqtt_str_free() must be called after calling this successfully * or not. */ lws_mqtt_stateful_primitive_return_t lws_mqtt_str_parse(lws_mqtt_str_t *s, const uint8_t **in, size_t *len) { const uint8_t *oin = *in; /* handle the length + allocation if needed */ while (*len && !s->len_valid && s->pos < 2) { s->len = (s->len << 8) | *((*in)++); (*len)--; oin = *in; if (++s->pos == 2) { if (s->len > s->limit) return LMSPR_FAILED_OVERSIZE; s->pos = 0; s->len_valid = 1; if (!s->len) /* do not need to allocate */ return LMSPR_COMPLETED; if (!s->buf) { s->buf = lws_malloc(s->len, __func__); if (!s->buf) return LMSPR_FAILED_OOM; s->needs_freeing = 1; } } } /* handle copying bulk data into allocation */ if (s->len_valid && *len) { uint16_t span = s->len - s->pos; if (span > *len) span = (uint16_t)*len; memcpy(s->buf + s->pos, *in, span); *in += span; s->pos += span; } *len -= *in - oin; return s->buf && s->pos == s->len ? LMSPR_COMPLETED : LMSPR_NEED_MORE; } int lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1, const lws_mqtt_str_t *bd2) { if (bd1->len != bd2->len) return 1; if (!!bd1->buf != !!bd2->buf) return 1; if (!bd1->buf && !bd2->buf) return 0; return memcmp(bd1->buf, bd2->buf, bd1->len); }