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