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