libwebsockets
Lightweight C library for HTML5 websockets
lws-mqtt.h
Go to the documentation of this file.
1/*
2 * libwebsockets - protocol - mqtt
3 *
4 * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * included from libwebsockets.h
25 */
26
27#ifndef _LWS_MQTT_H
28#define _LWS_MQTT_H 1
29
30struct _lws_mqtt_related;
31typedef struct _lws_mqtt_related lws_mqtt_related_t;
32struct lws_mqtt_str_st;
33typedef struct lws_mqtt_str_st lws_mqtt_str_t;
34
35#define MQTT_VER_3_1_1 4
36
37#define LWS_MQTT_FINAL_PART 1
38
39#define LWS_MQTT_MAX_AWSIOT_TOPICLEN 256
40#define LWS_MQTT_MAX_TOPICLEN 65535
41#define LWS_MQTT_MAX_CIDLEN 128
42#define LWS_MQTT_RANDOM_CIDLEN 23 /* 3.1.3.1-5: Server MUST... between
43 1 and 23 chars... */
45#define LWS_MQTT_SHADOW_MAX_THING_LEN 128
46#define LWS_MQTT_SHADOW_MAX_SHADOW_LEN 64
47#define LWS_MQTT_SHADOW_UPDATE_STR "/update"
48#define LWS_MQTT_SHADOW_DELETE_STR "/delete"
49#define LWS_MQTT_SHADOW_GET_STR "/get"
50#define LWS_MQTT_SHADOW_RESP_ACCEPTED_STR "/accepted"
51#define LWS_MQTT_SHADOW_RESP_REJECTED_STR "/rejected"
52#define LWS_MQTT_SHADOW_RESP_DELTA_STR "/delta"
53#define LWS_MQTT_SHADOW_RESP_DOCUMENT_STR "/documents"
54#define LWS_MQTT_SHADOW_UPDATE_ACCEPTED_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_ACCEPTED_STR
55#define LWS_MQTT_SHADOW_UPDATE_REJECTED_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_REJECTED_STR
56#define LWS_MQTT_SHADOW_UPDATE_DELTA_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_DELTA_STR
57#define LWS_MQTT_SHADOW_UPDATE_DOCUMENT_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_DOCUMENT_STR
58#define LWS_MQTT_SHADOW_DELETE_ACCEPTED_STR LWS_MQTT_SHADOW_DELETE_STR LWS_MQTT_SHADOW_RESP_ACCEPTED_STR
59#define LWS_MQTT_SHADOW_DELETE_REJECTED_STR LWS_MQTT_SHADOW_DELETE_STR LWS_MQTT_SHADOW_RESP_REJECTED_STR
60#define LWS_MQTT_SHADOW_GET_ACCEPTED_STR LWS_MQTT_SHADOW_GET_STR LWS_MQTT_SHADOW_RESP_ACCEPTED_STR
61#define LWS_MQTT_SHADOW_GET_REJECTED_STR LWS_MQTT_SHADOW_GET_STR LWS_MQTT_SHADOW_RESP_REJECTED_STR
62#define LWS_MQTT_SHADOW_PREFIX_FORMAT "$aws/things/%s"
63#define LWS_MQTT_SHADOW_NAMED_SHADOW_TOPIC_FORMAT LWS_MQTT_SHADOW_PREFIX_FORMAT "/shadow/name/%s%s"
64#define LWS_MQTT_SHADOW_UNNAMED_SHADOW_TOPIC_FORMAT LWS_MQTT_SHADOW_PREFIX_FORMAT "/shadow%s"
65#define LWS_MQTT_SHADOW_UNNAMED_TOPIC_MATCH "$aws/things/+/shadow/+"
66#define LWS_MQTT_SHADOW_NAMED_TOPIC_MATCH "$aws/things/+/shadow/name/+/+"
68typedef enum {
69 QOS0,
70 QOS1,
71 QOS2, /* not supported */
72 RESERVED_QOS_LEVEL,
73 FAILURE_QOS_LEVEL = 0x80
74} lws_mqtt_qos_levels_t;
76typedef union {
77 struct {
78 uint8_t retain:1;
79 uint8_t qos:2;
80 uint8_t dup:1;
81 uint8_t ctrl_pkt_type:4;
82 } flags;
83 uint8_t bits;
84} lws_mqtt_fixed_hdr_t;
85
86/*
87 * MQTT connection parameters, passed into struct
88 * lws_client_connect_info to establish a connection using
89 * lws_client_connect_via_info().
90*/
91typedef struct lws_mqtt_client_connect_param_s {
92 const char *client_id; /* Client ID */
93 uint16_t keep_alive; /* MQTT keep alive
94 interval in
95 seconds */
96 uint8_t clean_start:1; /* MQTT clean
97 session */
98 uint8_t client_id_nofree:1;
99 /**< do not free the client id */
100 uint8_t username_nofree:1;
101 /**< do not free the username */
102 uint8_t password_nofree:1;
103 /**< do not free the password */
104 struct {
105 const char *topic;
106 const char *message;
107 lws_mqtt_qos_levels_t qos;
108 uint8_t retain;
109 } will_param; /* MQTT LWT
110 parameters */
111 struct {
112 const char *topic;
113 const char *message;
114 lws_mqtt_qos_levels_t qos;
115 uint8_t retain;
116 } birth_param; /* MQTT Birth
117 parameters */
118 const char *username;
119 const char *password;
120 uint8_t aws_iot;
121} lws_mqtt_client_connect_param_t;
122
123/*
124 * MQTT publish parameters
126typedef struct lws_mqtt_publish_param_s {
127 char *topic; /* Topic Name */
128 uint16_t topic_len;
129 const void *payload; /* Publish Payload */
130 uint32_t payload_len; /* Size of the
131 complete payload */
132 uint32_t payload_pos; /* where we are in payload */
133 lws_mqtt_qos_levels_t qos;
134
135 /*--v-Following will be used by LWS-v--*/
136 uint16_t packet_id; /* Packet ID for QoS >
137 0 */
138 uint8_t dup:1; /* Retried PUBLISH,
139 for QoS > 0 */
140 uint8_t retain:1; /* Retained message */
141} lws_mqtt_publish_param_t;
143typedef struct topic_elem {
144 const char *name; /* Topic Name */
145 lws_mqtt_qos_levels_t qos; /* Requested QoS */
146
147 /*--v-Following will be used by LWS-v--*/
148 uint8_t acked;
149} lws_mqtt_topic_elem_t;
150
151/*
152 * MQTT publish parameters
154typedef struct lws_mqtt_subscribe_param_s {
155 uint32_t num_topics; /* Number of topics */
156 lws_mqtt_topic_elem_t *topic; /* Array of topic elements */
157
158 /*--v-Following will be used by LWS-v--*/
159 uint16_t packet_id;
160} lws_mqtt_subscribe_param_t;
162typedef enum {
163 LMQCP_RESERVED,
164 LMQCP_CTOS_CONNECT, /* Connection request */
165 LMQCP_STOC_CONNACK, /* Connection acknowledgment */
166 LMQCP_PUBLISH, /* Publish Message */
167 LMQCP_PUBACK, /* QoS 1: Publish acknowledgment */
168 LMQCP_PUBREC, /* QoS 2.1: Publish received */
169 LMQCP_PUBREL, /* QoS 2.2: Publish release */
170 LMQCP_PUBCOMP, /* QoS 2.3: Publish complete */
171 LMQCP_CTOS_SUBSCRIBE, /* Subscribe request */
172 LMQCP_STOC_SUBACK, /* Subscribe acknowledgment */
173 LMQCP_CTOS_UNSUBSCRIBE, /* Unsubscribe request */
174 LMQCP_STOC_UNSUBACK, /* Unsubscribe acknowledgment */
175 LMQCP_CTOS_PINGREQ, /* PING request */
176 LMQCP_STOC_PINGRESP, /* PONG response */
177 LMQCP_DISCONNECT, /* Disconnect notification */
178 LMQCP_AUTH /* Authentication exchange */
179} lws_mqtt_control_packet_t;
180
181/* flags from byte 8 of C_TO_S CONNECT */
182typedef enum {
183 LMQCFT_USERNAME_NOFREE = (1 << 10),
184 LMQCFT_PASSWORD_NOFREE = (1 << 9),
185 LMQCFT_CLIENT_ID_NOFREE = (1 << 8),
186 /* only the low 8 are standardized and go out in the protocol */
187 LMQCFT_USERNAME = (1 << 7),
188 LMQCFT_PASSWORD = (1 << 6),
189 LMQCFT_WILL_RETAIN = (1 << 5),
190 LMQCFT_WILL_QOS = (1 << 3),
191 LMQCFT_WILL_FLAG = (1 << 2),
192 LMQCFT_CLEAN_START = (1 << 1),
193 LMQCFT_RESERVED = (1 << 0),
195 LMQCFT_WILL_QOS_MASK = (3 << 3),
196} lws_mqtt_connect_flags_t;
197
198/* flags for S_TO_C CONNACK */
199typedef enum {
200 LMQCFT_SESSION_PRESENT = (1 << 0),
201} lws_mqtt_connack_flags_t;
203typedef enum {
204 LMQCP_REASON_SUCCESS = 0x00,
205 LMQCP_REASON_NORMAL_DISCONNECTION = 0x00,
206 LMQCP_REASON_GRANTED_QOS0 = 0x00,
207 LMQCP_REASON_GRANTED_QOS1 = 0x01,
208 LMQCP_REASON_GRANTED_QOS2 = 0x02,
209 LMQCP_REASON_DISCONNECT_WILL = 0x04,
210 LMQCP_REASON_NO_MATCHING_SUBSCRIBER = 0x10,
211 LMQCP_REASON_NO_SUBSCRIPTION_EXISTED = 0x11,
212 LMQCP_REASON_CONTINUE_AUTHENTICATION = 0x18,
213 LMQCP_REASON_RE_AUTHENTICATE = 0x19,
215 LMQCP_REASON_UNSPECIFIED_ERROR = 0x80,
216 LMQCP_REASON_MALFORMED_PACKET = 0x81,
217 LMQCP_REASON_PROTOCOL_ERROR = 0x82,
218 LMQCP_REASON_IMPLEMENTATION_SPECIFIC_ERROR = 0x83,
219
220 /* Begin - Error codes for CONNACK */
221 LMQCP_REASON_UNSUPPORTED_PROTOCOL = 0x84,
222 LMQCP_REASON_CLIENT_ID_INVALID = 0x85,
223 LMQCP_REASON_BAD_CREDENTIALS = 0x86,
224 LMQCP_REASON_NOT_AUTHORIZED = 0x87,
225 /* End - Error codes for CONNACK */
227 LMQCP_REASON_SERVER_UNAVAILABLE = 0x88,
228 LMQCP_REASON_SERVER_BUSY = 0x89,
229 LMQCP_REASON_BANNED = 0x8a,
230 LMQCP_REASON_SERVER_SHUTTING_DOWN = 0x8b,
231 LMQCP_REASON_BAD_AUTHENTICATION_METHOD = 0x8c,
232 LMQCP_REASON_KEEPALIVE_TIMEOUT = 0x8d,
233 LMQCP_REASON_SESSION_TAKEN_OVER = 0x8e,
234 LMQCP_REASON_TOPIC_FILTER_INVALID = 0x8f,
235 LMQCP_REASON_TOPIC_NAME_INVALID = 0x90,
236 LMQCP_REASON_PACKET_ID_IN_USE = 0x91,
237 LMQCP_REASON_PACKET_ID_NOT_FOUND = 0x92,
238 LMQCP_REASON_MAX_RX_EXCEEDED = 0x93,
239 LMQCP_REASON_TOPIC_ALIAS_INVALID = 0x94,
240 LMQCP_REASON_PACKET_TOO_LARGE = 0x95,
241 LMQCP_REASON_RATELIMIT = 0x96,
242 LMQCP_REASON_QUOTA_EXCEEDED = 0x97,
243 LMQCP_REASON_ADMINISTRATIVE_ACTION = 0x98,
244 LMQCP_REASON_PAYLOAD_FORMAT_INVALID = 0x99,
245 LMQCP_REASON_RETAIN_NOT_SUPPORTED = 0x9a,
246 LMQCP_REASON_QOS_NOT_SUPPORTED = 0x9b,
247 LMQCP_REASON_USE_ANOTHER_SERVER = 0x9c,
248 LMQCP_REASON_SERVER_MOVED = 0x9d,
249 LMQCP_REASON_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9e,
250 LMQCP_REASON_CONNECTION_RATE_EXCEEDED = 0x9f,
251 LMQCP_REASON_MAXIMUM_CONNECT_TIME = 0xa0,
252 LMQCP_REASON_SUBSCRIPTION_IDS_NOT_SUPPORTED = 0xa1,
253 LMQCP_REASON_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xa2,
254} lws_mqtt_reason_t;
256typedef enum {
257 LMQPROP_INVALID,
258 LMQPROP_PAYLOAD_FORMAT_INDICATOR = 0x01,
259 LMQPROP_MESSAGE_EXPIRY_INTERVAL = 0x02,
260 LMQPROP_CONTENT_TYPE = 0x03,
261 LMQPROP_RESPONSE_TOPIC = 0x08,
262 LMQPROP_CORRELATION_DATA = 0x09,
263 LMQPROP_SUBSCRIPTION_IDENTIFIER = 0x0b,
264 LMQPROP_SESSION_EXPIRY_INTERVAL = 0x11,
265 LMQPROP_ASSIGNED_CLIENT_IDENTIFIER = 0x12,
266 LMQPROP_SERVER_KEEP_ALIVE = 0x13,
267 LMQPROP_AUTHENTICATION_METHOD = 0x15,
268 LMQPROP_AUTHENTICATION_DATA = 0x16,
269 LMQPROP_REQUEST_PROBLEM_INFORMATION = 0x17,
270 LMQPROP_WILL_DELAY_INTERVAL = 0x18,
271 LMQPROP_REQUEST_RESPONSE_INFORMATION = 0x19,
272 LMQPROP_RESPONSE_INFORMATION = 0x1a,
273 LMQPROP_SERVER_REFERENCE = 0x1c,
274 LMQPROP_REASON_STRING = 0x1f,
275 LMQPROP_RECEIVE_MAXIMUM = 0x21,
276 LMQPROP_TOPIC_ALIAS_MAXIMUM = 0x22,
277 LMQPROP_TOPIC_ALIAS = 0x23,
278 LMQPROP_MAXIMUM_QOS = 0x24,
279 LMQPROP_RETAIN_AVAILABLE = 0x25,
280 LMQPROP_USER_PROPERTY = 0x26,
281 LMQPROP_MAXIMUM_PACKET_SIZE = 0x27,
282 LMQPROP_WILDCARD_SUBSCRIPTION_AVAIL = 0x28,
283 LMQPROP_SUBSCRIPTION_IDENTIFIER_AVAIL = 0x29,
284 LMQPROP_SHARED_SUBSCRIPTION_AVAIL = 0x2a
285} lws_mqtt_property;
286
287int
288lws_read_mqtt(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
289
290/* returns 0 if bd1 and bd2 are "the same", that includes empty, else nonzero */
291LWS_VISIBLE LWS_EXTERN int
292lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1, const lws_mqtt_str_t *bd2);
293
294LWS_VISIBLE LWS_EXTERN void
295lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf);
296
297LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
298lws_mqtt_str_create(uint16_t lim);
299
300LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
301lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim);
302
303LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
304lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim);
305
306LWS_VISIBLE LWS_EXTERN uint8_t *
307lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget);
308
309LWS_VISIBLE LWS_EXTERN int
310lws_mqtt_str_advance(lws_mqtt_str_t *s, int n);
311
312LWS_VISIBLE LWS_EXTERN void
313lws_mqtt_str_free(lws_mqtt_str_t **s);
314
315
316/**
317 * lws_mqtt_client_send_publish() - lws_write a publish packet
318 *
319 * \param wsi: the mqtt child wsi
320 * \param pub: additional information on what we're publishing
321 * \param buf: payload to send
322 * \param len: length of data in buf
323 * \param final: flag indicating this is the last part
324 *
325 * Issues part of, or the whole of, a PUBLISH frame. The first part of the
326 * frame contains the header, and uses the .qos and .payload_len parts of \p pub
327 * since MQTT requires the frame to specify the PUBLISH message length at the
328 * start. The \p len paramter may be less than \p pub.payload_len, in which
329 * case subsequent calls with more payload are needed to complete the frame.
330 *
331 * Although the connection is stuck waiting for the remainder, in that it can't
332 * issue any other frames until the current one is completed, lws returns to the
333 * event loop normally and can continue the calls with additional payload even
334 * for huge frames as the data becomes available, consistent with timeout needs
335 * and latency to start any new frame (even, eg, related to ping / pong).
336 *
337 * If you're sending large frames, the OS will typically not allow the data to
338 * be sent all at once to kernel side. So you should ideally cut the payload
339 * up into 1 or 2- mtu sized chunks and send that.
340 *
341 * Final should be set when you're calling with the last part of the payload.
342 */
343LWS_VISIBLE LWS_EXTERN int
344lws_mqtt_client_send_publish(struct lws *wsi, lws_mqtt_publish_param_t *pub,
345 const void *buf, uint32_t len, int final);
346
347/**
348 * lws_mqtt_client_send_subcribe() - lws_write a subscribe packet
349 *
350 * \param wsi: the mqtt child wsi
351 * \param sub: which topic(s) we want to subscribe to
352 *
353 * For topics other child streams have not already subscribed to, send a packet
354 * to the server asking to subscribe to them. If all topics listed are already
355 * subscribed to be the shared network connection, just trigger the
356 * LWS_CALLBACK_MQTT_SUBSCRIBED callback as if a SUBACK had come.
357 *
358 * \p sub doesn't need to exist after the return from this function.
359 */
360LWS_VISIBLE LWS_EXTERN int
361lws_mqtt_client_send_subcribe(struct lws *wsi, lws_mqtt_subscribe_param_t *sub);
362
363/**
364 * lws_mqtt_client_send_unsubcribe() - lws_write a unsubscribe packet
365 *
366 * \param wsi: the mqtt child wsi
367 * \param sub: which topic(s) we want to unsubscribe from
368 *
369 * For topics other child streams are not subscribed to, send a packet
370 * to the server asking to unsubscribe from them. If all topics
371 * listed are already subscribed by other child streams on the shared
372 * network connection, just trigger the LWS_CALLBACK_MQTT_UNSUBSCRIBED
373 * callback as if a UNSUBACK had come.
374 *
375 * \p unsub doesn't need to exist after the return from this function.
376 */
377LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
378lws_mqtt_client_send_unsubcribe(struct lws *wsi,
379 const lws_mqtt_subscribe_param_t *unsub);
380
381#endif /* _LWS_MQTT_H */
struct lws_mqtt_str_st lws_mqtt_str_t
Definition: lws-mqtt.h:33
#define LWS_MQTT_SHADOW_DELETE_STR
Definition: lws-mqtt.h:47
#define LWS_MQTT_SHADOW_UPDATE_STR
Definition: lws-mqtt.h:46
#define LWS_MQTT_SHADOW_PREFIX_FORMAT
Definition: lws-mqtt.h:61
#define LWS_MQTT_SHADOW_GET_STR
Definition: lws-mqtt.h:48
struct _lws_mqtt_related lws_mqtt_related_t
Definition: lws-mqtt.h:31
#define LWS_MQTT_SHADOW_RESP_DOCUMENT_STR
Definition: lws-mqtt.h:52
#define LWS_MQTT_SHADOW_RESP_DELTA_STR
Definition: lws-mqtt.h:51
#define LWS_MQTT_SHADOW_RESP_ACCEPTED_STR
Definition: lws-mqtt.h:49
#define LWS_MQTT_SHADOW_RESP_REJECTED_STR
Definition: lws-mqtt.h:50