• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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/+/+"
67 
68 typedef enum {
69 	QOS0,
70 	QOS1,
71 	QOS2,				/* not supported */
72 	RESERVED_QOS_LEVEL,
73 	FAILURE_QOS_LEVEL = 0x80
74 } lws_mqtt_qos_levels_t;
75 
76 typedef 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 */
91 typedef 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
125 */
126 typedef 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;
142 
143 typedef 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
153 */
154 typedef 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;
161 
162 typedef 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 */
182 typedef 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),
194 
195 	LMQCFT_WILL_QOS_MASK					= (3 << 3),
196 } lws_mqtt_connect_flags_t;
197 
198 /* flags for S_TO_C CONNACK */
199 typedef enum {
200 	LMQCFT_SESSION_PRESENT					= (1 << 0),
201 } lws_mqtt_connack_flags_t;
202 
203 typedef 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,
214 
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 */
226 
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;
255 
256 typedef 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 
287 int
288 lws_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 */
291 LWS_VISIBLE LWS_EXTERN int
292 lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1, const lws_mqtt_str_t *bd2);
293 
294 LWS_VISIBLE LWS_EXTERN void
295 lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf);
296 
297 LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
298 lws_mqtt_str_create(uint16_t lim);
299 
300 LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
301 lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim);
302 
303 LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
304 lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim);
305 
306 LWS_VISIBLE LWS_EXTERN uint8_t *
307 lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget);
308 
309 LWS_VISIBLE LWS_EXTERN int
310 lws_mqtt_str_advance(lws_mqtt_str_t *s, int n);
311 
312 LWS_VISIBLE LWS_EXTERN void
313 lws_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  */
343 LWS_VISIBLE LWS_EXTERN int
344 lws_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  */
360 LWS_VISIBLE LWS_EXTERN int
361 lws_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  */
377 LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
378 lws_mqtt_client_send_unsubcribe(struct lws *wsi,
379 				const lws_mqtt_subscribe_param_t *unsub);
380 
381 #endif /* _LWS_MQTT_H */
382