lws-mqtt.h (14168B)
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... */ 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 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; 75 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 125*/ 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; 142 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 153*/ 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; 161 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), 194 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; 202 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, 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 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 */