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 
30 struct _lws_mqtt_related;
31 typedef struct _lws_mqtt_related lws_mqtt_related_t;
32 struct lws_mqtt_str_st;
33 typedef 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... */
44 
45 typedef enum {
46  QOS0,
47  QOS1,
48  QOS2, /* not supported */
49  RESERVED_QOS_LEVEL,
50  FAILURE_QOS_LEVEL = 0x80
51 } lws_mqtt_qos_levels_t;
52 
53 typedef union {
54  struct {
55  uint8_t retain:1;
56  uint8_t qos:2;
57  uint8_t dup:1;
58  uint8_t ctrl_pkt_type:4;
59  } flags;
60  uint8_t bits;
61 } lws_mqtt_fixed_hdr_t;
62 
63 /*
64  * MQTT connection parameters, passed into struct
65  * lws_client_connect_info to establish a connection using
66  * lws_client_connect_via_info().
67 */
68 typedef struct lws_mqtt_client_connect_param_s {
69  const char *client_id; /* Client ID */
70  uint16_t keep_alive; /* MQTT keep alive
71  interval in
72  seconds */
73  uint8_t clean_start:1; /* MQTT clean
74  session */
75  uint8_t client_id_nofree:1;
76  /**< do not free the client id */
77  uint8_t username_nofree:1;
78  /**< do not free the username */
79  uint8_t password_nofree:1;
80  /**< do not free the password */
81  struct {
82  const char *topic;
83  const char *message;
84  lws_mqtt_qos_levels_t qos;
85  uint8_t retain;
86  } will_param; /* MQTT LWT
87  parameters */
88  struct {
89  const char *topic;
90  const char *message;
91  lws_mqtt_qos_levels_t qos;
92  uint8_t retain;
93  } birth_param; /* MQTT Birth
94  parameters */
95  const char *username;
96  const char *password;
97  uint8_t aws_iot;
98 } lws_mqtt_client_connect_param_t;
99 
100 /*
101  * MQTT publish parameters
102 */
103 typedef struct lws_mqtt_publish_param_s {
104  char *topic; /* Topic Name */
105  uint16_t topic_len;
106  const void *payload; /* Publish Payload */
107  uint32_t payload_len; /* Size of the
108  complete payload */
109  uint32_t payload_pos; /* where we are in payload */
110  lws_mqtt_qos_levels_t qos;
111 
112  /*--v-Following will be used by LWS-v--*/
113  uint16_t packet_id; /* Packet ID for QoS >
114  0 */
115  uint8_t dup:1; /* Retried PUBLISH,
116  for QoS > 0 */
117  uint8_t retain:1; /* Retained message */
118 } lws_mqtt_publish_param_t;
119 
120 typedef struct topic_elem {
121  const char *name; /* Topic Name */
122  lws_mqtt_qos_levels_t qos; /* Requested QoS */
123 
124  /*--v-Following will be used by LWS-v--*/
125  uint8_t acked;
126 } lws_mqtt_topic_elem_t;
127 
128 /*
129  * MQTT publish parameters
130 */
131 typedef struct lws_mqtt_subscribe_param_s {
132  uint32_t num_topics; /* Number of topics */
133  lws_mqtt_topic_elem_t *topic; /* Array of topic elements */
134 
135  /*--v-Following will be used by LWS-v--*/
136  uint16_t packet_id;
137 } lws_mqtt_subscribe_param_t;
138 
139 typedef enum {
140  LMQCP_RESERVED,
141  LMQCP_CTOS_CONNECT, /* Connection request */
142  LMQCP_STOC_CONNACK, /* Connection acknowledgment */
143  LMQCP_PUBLISH, /* Publish Message */
144  LMQCP_PUBACK, /* QoS 1: Publish acknowledgment */
145  LMQCP_PUBREC, /* QoS 2.1: Publish received */
146  LMQCP_PUBREL, /* QoS 2.2: Publish release */
147  LMQCP_PUBCOMP, /* QoS 2.3: Publish complete */
148  LMQCP_CTOS_SUBSCRIBE, /* Subscribe request */
149  LMQCP_STOC_SUBACK, /* Subscribe acknowledgment */
150  LMQCP_CTOS_UNSUBSCRIBE, /* Unsubscribe request */
151  LMQCP_STOC_UNSUBACK, /* Unsubscribe acknowledgment */
152  LMQCP_CTOS_PINGREQ, /* PING request */
153  LMQCP_STOC_PINGRESP, /* PONG response */
154  LMQCP_DISCONNECT, /* Disconnect notification */
155  LMQCP_AUTH /* Authentication exchange */
156 } lws_mqtt_control_packet_t;
157 
158 /* flags from byte 8 of C_TO_S CONNECT */
159 typedef enum {
160  LMQCFT_USERNAME_NOFREE = (1 << 10),
161  LMQCFT_PASSWORD_NOFREE = (1 << 9),
162  LMQCFT_CLIENT_ID_NOFREE = (1 << 8),
163  /* only the low 8 are standardized and go out in the protocol */
164  LMQCFT_USERNAME = (1 << 7),
165  LMQCFT_PASSWORD = (1 << 6),
166  LMQCFT_WILL_RETAIN = (1 << 5),
167  LMQCFT_WILL_QOS = (1 << 3),
168  LMQCFT_WILL_FLAG = (1 << 2),
169  LMQCFT_CLEAN_START = (1 << 1),
170  LMQCFT_RESERVED = (1 << 0),
171 
172  LMQCFT_WILL_QOS_MASK = (3 << 3),
173 } lws_mqtt_connect_flags_t;
174 
175 /* flags for S_TO_C CONNACK */
176 typedef enum {
177  LMQCFT_SESSION_PRESENT = (1 << 0),
178 } lws_mqtt_connack_flags_t;
179 
180 typedef enum {
181  LMQCP_REASON_SUCCESS = 0x00,
182  LMQCP_REASON_NORMAL_DISCONNECTION = 0x00,
183  LMQCP_REASON_GRANTED_QOS0 = 0x00,
184  LMQCP_REASON_GRANTED_QOS1 = 0x01,
185  LMQCP_REASON_GRANTED_QOS2 = 0x02,
186  LMQCP_REASON_DISCONNECT_WILL = 0x04,
187  LMQCP_REASON_NO_MATCHING_SUBSCRIBER = 0x10,
188  LMQCP_REASON_NO_SUBSCRIPTION_EXISTED = 0x11,
189  LMQCP_REASON_CONTINUE_AUTHENTICATION = 0x18,
190  LMQCP_REASON_RE_AUTHENTICATE = 0x19,
191 
192  LMQCP_REASON_UNSPECIFIED_ERROR = 0x80,
193  LMQCP_REASON_MALFORMED_PACKET = 0x81,
194  LMQCP_REASON_PROTOCOL_ERROR = 0x82,
195  LMQCP_REASON_IMPLEMENTATION_SPECIFIC_ERROR = 0x83,
196 
197  /* Begin - Error codes for CONNACK */
198  LMQCP_REASON_UNSUPPORTED_PROTOCOL = 0x84,
199  LMQCP_REASON_CLIENT_ID_INVALID = 0x85,
200  LMQCP_REASON_BAD_CREDENTIALS = 0x86,
201  LMQCP_REASON_NOT_AUTHORIZED = 0x87,
202  /* End - Error codes for CONNACK */
203 
204  LMQCP_REASON_SERVER_UNAVAILABLE = 0x88,
205  LMQCP_REASON_SERVER_BUSY = 0x89,
206  LMQCP_REASON_BANNED = 0x8a,
207  LMQCP_REASON_SERVER_SHUTTING_DOWN = 0x8b,
208  LMQCP_REASON_BAD_AUTHENTICATION_METHOD = 0x8c,
209  LMQCP_REASON_KEEPALIVE_TIMEOUT = 0x8d,
210  LMQCP_REASON_SESSION_TAKEN_OVER = 0x8e,
211  LMQCP_REASON_TOPIC_FILTER_INVALID = 0x8f,
212  LMQCP_REASON_TOPIC_NAME_INVALID = 0x90,
213  LMQCP_REASON_PACKET_ID_IN_USE = 0x91,
214  LMQCP_REASON_PACKET_ID_NOT_FOUND = 0x92,
215  LMQCP_REASON_MAX_RX_EXCEEDED = 0x93,
216  LMQCP_REASON_TOPIC_ALIAS_INVALID = 0x94,
217  LMQCP_REASON_PACKET_TOO_LARGE = 0x95,
218  LMQCP_REASON_RATELIMIT = 0x96,
219  LMQCP_REASON_QUOTA_EXCEEDED = 0x97,
220  LMQCP_REASON_ADMINISTRATIVE_ACTION = 0x98,
221  LMQCP_REASON_PAYLOAD_FORMAT_INVALID = 0x99,
222  LMQCP_REASON_RETAIN_NOT_SUPPORTED = 0x9a,
223  LMQCP_REASON_QOS_NOT_SUPPORTED = 0x9b,
224  LMQCP_REASON_USE_ANOTHER_SERVER = 0x9c,
225  LMQCP_REASON_SERVER_MOVED = 0x9d,
226  LMQCP_REASON_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9e,
227  LMQCP_REASON_CONNECTION_RATE_EXCEEDED = 0x9f,
228  LMQCP_REASON_MAXIMUM_CONNECT_TIME = 0xa0,
229  LMQCP_REASON_SUBSCRIPTION_IDS_NOT_SUPPORTED = 0xa1,
230  LMQCP_REASON_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xa2,
231 } lws_mqtt_reason_t;
232 
233 typedef enum {
234  LMQPROP_INVALID,
235  LMQPROP_PAYLOAD_FORMAT_INDICATOR = 0x01,
236  LMQPROP_MESSAGE_EXPIRY_INTERVAL = 0x02,
237  LMQPROP_CONTENT_TYPE = 0x03,
238  LMQPROP_RESPONSE_TOPIC = 0x08,
239  LMQPROP_CORRELATION_DATA = 0x09,
240  LMQPROP_SUBSCRIPTION_IDENTIFIER = 0x0b,
241  LMQPROP_SESSION_EXPIRY_INTERVAL = 0x11,
242  LMQPROP_ASSIGNED_CLIENT_IDENTIFIER = 0x12,
243  LMQPROP_SERVER_KEEP_ALIVE = 0x13,
244  LMQPROP_AUTHENTICATION_METHOD = 0x15,
245  LMQPROP_AUTHENTICATION_DATA = 0x16,
246  LMQPROP_REQUEST_PROBLEM_INFORMATION = 0x17,
247  LMQPROP_WILL_DELAY_INTERVAL = 0x18,
248  LMQPROP_REQUEST_RESPONSE_INFORMATION = 0x19,
249  LMQPROP_RESPONSE_INFORMATION = 0x1a,
250  LMQPROP_SERVER_REFERENCE = 0x1c,
251  LMQPROP_REASON_STRING = 0x1f,
252  LMQPROP_RECEIVE_MAXIMUM = 0x21,
253  LMQPROP_TOPIC_ALIAS_MAXIMUM = 0x22,
254  LMQPROP_TOPIC_ALIAS = 0x23,
255  LMQPROP_MAXIMUM_QOS = 0x24,
256  LMQPROP_RETAIN_AVAILABLE = 0x25,
257  LMQPROP_USER_PROPERTY = 0x26,
258  LMQPROP_MAXIMUM_PACKET_SIZE = 0x27,
259  LMQPROP_WILDCARD_SUBSCRIPTION_AVAIL = 0x28,
260  LMQPROP_SUBSCRIPTION_IDENTIFIER_AVAIL = 0x29,
261  LMQPROP_SHARED_SUBSCRIPTION_AVAIL = 0x2a
262 } lws_mqtt_property;
263 
264 int
265 lws_read_mqtt(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
266 
267 /* returns 0 if bd1 and bd2 are "the same", that includes empty, else nonzero */
268 LWS_VISIBLE LWS_EXTERN int
269 lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1, const lws_mqtt_str_t *bd2);
270 
271 LWS_VISIBLE LWS_EXTERN void
272 lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf);
273 
274 LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
275 lws_mqtt_str_create(uint16_t lim);
276 
277 LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
278 lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim);
279 
280 LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
281 lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim);
282 
283 LWS_VISIBLE LWS_EXTERN uint8_t *
284 lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget);
285 
286 LWS_VISIBLE LWS_EXTERN int
287 lws_mqtt_str_advance(lws_mqtt_str_t *s, int n);
288 
289 LWS_VISIBLE LWS_EXTERN void
290 lws_mqtt_str_free(lws_mqtt_str_t **s);
291 
292 
293 /**
294  * lws_mqtt_client_send_publish() - lws_write a publish packet
295  *
296  * \param wsi: the mqtt child wsi
297  * \param pub: additional information on what we're publishing
298  * \param buf: payload to send
299  * \param len: length of data in buf
300  * \param final: flag indicating this is the last part
301  *
302  * Issues part of, or the whole of, a PUBLISH frame. The first part of the
303  * frame contains the header, and uses the .qos and .payload_len parts of \p pub
304  * since MQTT requires the frame to specify the PUBLISH message length at the
305  * start. The \p len paramter may be less than \p pub.payload_len, in which
306  * case subsequent calls with more payload are needed to complete the frame.
307  *
308  * Although the connection is stuck waiting for the remainder, in that it can't
309  * issue any other frames until the current one is completed, lws returns to the
310  * event loop normally and can continue the calls with additional payload even
311  * for huge frames as the data becomes available, consistent with timeout needs
312  * and latency to start any new frame (even, eg, related to ping / pong).
313  *
314  * If you're sending large frames, the OS will typically not allow the data to
315  * be sent all at once to kernel side. So you should ideally cut the payload
316  * up into 1 or 2- mtu sized chunks and send that.
317  *
318  * Final should be set when you're calling with the last part of the payload.
319  */
320 LWS_VISIBLE LWS_EXTERN int
321 lws_mqtt_client_send_publish(struct lws *wsi, lws_mqtt_publish_param_t *pub,
322  const void *buf, uint32_t len, int final);
323 
324 /**
325  * lws_mqtt_client_send_subcribe() - lws_write a subscribe packet
326  *
327  * \param wsi: the mqtt child wsi
328  * \param sub: which topic(s) we want to subscribe to
329  *
330  * For topics other child streams have not already subscribed to, send a packet
331  * to the server asking to subscribe to them. If all topics listed are already
332  * subscribed to be the shared network connection, just trigger the
333  * LWS_CALLBACK_MQTT_SUBSCRIBED callback as if a SUBACK had come.
334  *
335  * \p sub doesn't need to exist after the return from this function.
336  */
337 LWS_VISIBLE LWS_EXTERN int
338 lws_mqtt_client_send_subcribe(struct lws *wsi, lws_mqtt_subscribe_param_t *sub);
339 
340 /**
341  * lws_mqtt_client_send_unsubcribe() - lws_write a unsubscribe packet
342  *
343  * \param wsi: the mqtt child wsi
344  * \param sub: which topic(s) we want to unsubscribe from
345  *
346  * For topics other child streams are not subscribed to, send a packet
347  * to the server asking to unsubscribe from them. If all topics
348  * listed are already subscribed by other child streams on the shared
349  * network connection, just trigger the LWS_CALLBACK_MQTT_UNSUBSCRIBED
350  * callback as if a UNSUBACK had come.
351  *
352  * \p unsub doesn't need to exist after the return from this function.
353  */
354 LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
355 lws_mqtt_client_send_unsubcribe(struct lws *wsi,
356  const lws_mqtt_subscribe_param_t *unsub);
357 
358 #endif /* _LWS_MQTT_H */