├── .gitignore ├── Arduino ├── MqttsnClient │ ├── MqttsnClient.ino │ ├── _MeshBee.ino │ └── config.h └── libraries │ └── mqttsn │ ├── mqttsn-messages.cpp │ ├── mqttsn-messages.h │ └── mqttsn.h ├── README.md ├── RSMB ├── Messages.1.3.0.2 ├── broker_mqtts └── standalone.conf ├── S7-1200 └── MQTT_Demo_V14.zap14 ├── serial-mqtts ├── MB_PacketTest.py └── ser_redir.py ├── subscribe.py └── websocket-mqtt ├── Makefile ├── websocket.c ├── websocket.h ├── websockify └── websockify.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | -------------------------------------------------------------------------------- /Arduino/MqttsnClient/MqttsnClient.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "config.h" 7 | 8 | #define TOPIC_PUB "arduino/temp" 9 | #define TOPIC_SUB "arduino/temp" 10 | 11 | MQTTSN mqttsn; 12 | uint16_t u16TopicPubID; 13 | uint16_t u16TopicSubID; 14 | uint8_t u8Counter; 15 | SoftwareSerial DbgSer(10, 11); // RX, TX 16 | 17 | uint8_t FrameBufferIn[API_FRAME_LEN]; 18 | uint8_t FrameBufferOut[API_FRAME_LEN]; 19 | 20 | void setup() 21 | { 22 | DbgSer.begin(9600); 23 | Serial.begin(115200); 24 | 25 | ADMUX = 0xC8; // turn on internal reference, right-shift ADC buffer, ADC channel = internal temp sensor 26 | delay(100); // wait a sec for the analog reference to stabilize 27 | 28 | u8Counter = 1; 29 | DbgSer.println("Setup done"); 30 | } 31 | 32 | 33 | void loop() 34 | { 35 | // Check serila data 36 | CheckSerial(); 37 | 38 | // ------- MQTT-SN publis logic ------- 39 | // Busy? 40 | if (mqttsn.wait_for_response()) 41 | { 42 | // Busy. exit 43 | return; 44 | } 45 | 46 | // Connected? 47 | if (!mqttsn.connected()) 48 | { 49 | // Not connected - connect 50 | mqttsn.connect(0, 10, "test1758"); // Flags=0, Duration=10 51 | DbgSer.println("Connect sent"); 52 | return; 53 | } 54 | 55 | // Topic registered? 56 | uint8_t index; 57 | u16TopicPubID = mqttsn.find_topic_id(TOPIC_PUB, &index); 58 | if (u16TopicPubID == 0xffff) 59 | { 60 | // Topic is not registered yet 61 | mqttsn.register_topic(TOPIC_PUB); 62 | DbgSer.println("Reg top sent"); 63 | return; 64 | } 65 | 66 | // Topic is alredy registered, publish 67 | char str[50]; 68 | char temp[10]; 69 | float t = averageTemperature(); 70 | itoa(t, temp, 10); 71 | sprintf(str, "%s (%i)", temp, u8Counter); 72 | DbgSer.print("Top id:"); 73 | DbgSer.print(u16TopicPubID); 74 | DbgSer.print("; Val:"); 75 | DbgSer.println(str); 76 | mqttsn.publish(0, u16TopicPubID, str, strlen(str)); // Flags=0 77 | u8Counter++; 78 | delay(2000); 79 | 80 | // ------- MQTT-SN subscribe logic ------- 81 | u16TopicSubID = mqttsn.find_topic_id(TOPIC_SUB, &index); 82 | if (u16TopicSubID == 0xffff) 83 | { 84 | // Topic is not registered yet 85 | mqttsn.subscribe_by_name(0, TOPIC_SUB); // Flags=0 86 | DbgSer.println("Sub top sent"); 87 | return; 88 | } 89 | delay(2000); 90 | } 91 | 92 | 93 | // gwinfo message callback 94 | void MQTTSN_gwinfo_handler(const msg_gwinfo* msg) 95 | { 96 | // Got a gateway response 97 | // The frame is still in the buffer - parse it 98 | 99 | DbgSer.println("GW info hand"); 100 | } 101 | 102 | 103 | void MQTTSN_publish_handler(const msg_publish* msg) 104 | { 105 | DbgSer.println("Sub pub hand"); 106 | } 107 | 108 | 109 | // Callback funciton to send serial data 110 | void MQTTSN_serial_send(uint8_t* message_buffer, int length) 111 | { 112 | // Assuming that our gateway is at address 0 (coordinator) 113 | int len = MB_FrameCreate (message_buffer, length, 0x0000, FrameBufferOut, sizeof(FrameBufferOut), false); 114 | if (len > 0) 115 | { 116 | Serial.write(FrameBufferOut, len); 117 | Serial.flush(); 118 | } 119 | } 120 | 121 | 122 | // Serial event interrupt 123 | void CheckSerial() 124 | { 125 | if (Serial.available() > 0) 126 | { 127 | // Wait till we got the whole packet or timeout 128 | long Start = millis(); 129 | int cnt =0; 130 | while (cnt < API_FRAME_LEN && (millis() - Start) < 100) 131 | { 132 | while (Serial.available() > 0) 133 | { 134 | FrameBufferIn[cnt++] = (uint8_t)Serial.read(); 135 | 136 | // Reset timeout counter 137 | Start = millis(); 138 | } 139 | } 140 | 141 | if (cnt == API_FRAME_LEN) 142 | { 143 | // Got the whole packet 144 | DbgSer.print("From ser:"); 145 | for (int i=0; i 0) 156 | { 157 | DbgSer.print("Ser payload:"); 158 | for (int i=0; i 2 | #include 3 | #include 4 | #include "config.h" 5 | 6 | #define API_DATA_PACKET 0x02 7 | #define API_START_DELIMITER 0x7E 8 | #define OPTION_CAST_MASK 0x40 //option unicast or broadcast MASK 9 | #define OPTION_ACK_MASK 0x80 // option ACK or not MASK 10 | 11 | uint8_t FrameID = 0; 12 | 13 | // Create MeshBee frame (return frame lenght) 14 | int MB_FrameCreate(uint8_t* data, int data_len, uint16_t dest_addr, uint8_t* frame, int frame_max_len, bool broadcast) 15 | { 16 | // frame buffer is big enough? 17 | if ( frame_max_len < API_FRAME_LEN ) 18 | { 19 | return -1; 20 | } 21 | 22 | // data is too long? 23 | // TODO: Split in multiple packets? 24 | if (data_len > API_DATA_LEN) 25 | { 26 | return -2; 27 | } 28 | 29 | // Frame buffer is fine. Clear it 30 | memset (frame, 0, frame_max_len); 31 | 32 | // Header 33 | frame[0] = API_START_DELIMITER; // Delimiter 34 | frame[1] = API_PAY_LEN; // Length of the payload 35 | frame[2] = API_DATA_PACKET; // API ID 36 | 37 | // Payload 38 | uint8_t cs = 0; // CS=Sum of the payload 39 | cs += frame[3] = FrameID++; // frame id 40 | if (broadcast) 41 | { 42 | cs += frame[4] = OPTION_CAST_MASK; // option 43 | } 44 | cs += frame[5] = data_len; // data length 45 | 46 | // Data 47 | for (int i=0; i> 8) && 0xFF; // Unicast address (16 bits) 52 | cs += frame[7 + API_DATA_LEN] = (dest_addr >> 0) && 0xFF; // Unicast address (16 bits) 53 | frame[8 + API_DATA_LEN] = cs; 54 | 55 | return API_FRAME_LEN; 56 | } 57 | 58 | 59 | // Parse MeshBee frame (return data length) 60 | int MB_FrameParse(uint8_t* frame, int frame_len, uint8_t* data, int data_max_len, uint16_t* src_addr) 61 | { 62 | // got a full frame? 63 | if ( frame_len != API_FRAME_LEN ) 64 | { 65 | // TODO: May be a valid frame, keep reading? 66 | return -1; 67 | } 68 | 69 | // Delimeter? 70 | if (frame[0] != API_START_DELIMITER) // Delimiter 71 | { 72 | return -2; 73 | } 74 | 75 | // Payload length? 76 | if (frame[1] != API_PAY_LEN) // Length of the payload 77 | { 78 | return -3; 79 | } 80 | 81 | // Right API ID? 82 | if (frame[2] != API_DATA_PACKET) // API ID 83 | { 84 | return -4; 85 | } 86 | 87 | // Payload 88 | int len; 89 | uint8_t cs = 0; // CS=Sum of the payload 90 | cs += frame[3]; // frame id 91 | cs += frame[4]; // option 92 | 93 | // Enough space in the payload buffer? 94 | cs += len = frame[5]; 95 | if (len > data_max_len) 96 | { 97 | return -5; 98 | } 99 | 100 | // Clear output buffer 101 | memset (data, 0, data_max_len); 102 | 103 | // Copy data to the buffer 104 | for (int i=0; i 28 | 29 | #include "mqttsn-messages.h" 30 | #include "mqttsn.h" 31 | 32 | #ifdef USE_RF12 33 | #include 34 | #endif 35 | 36 | #if !(USE_RF12 || USE_SERIAL) 37 | #error "You really should define one or both of USE_RF12 or USE_SERIAL." 38 | #endif 39 | 40 | MQTTSN::MQTTSN() : 41 | waiting_for_response(false), 42 | _connected(false), 43 | _message_id(0), 44 | topic_count(0), 45 | _gateway_id(0), 46 | _response_timer(0), 47 | _response_retries(0) 48 | { 49 | memset(topic_table, 0, sizeof(topic) * MAX_TOPICS); 50 | memset(message_buffer, 0, MAX_BUFFER_SIZE); 51 | memset(response_buffer, 0, MAX_BUFFER_SIZE); 52 | } 53 | 54 | MQTTSN::~MQTTSN() { 55 | } 56 | 57 | bool MQTTSN::wait_for_response() { 58 | if (waiting_for_response) { 59 | // TODO: Watch out for overflow. 60 | if ((millis() - _response_timer) > (T_RETRY * 1000L)) { 61 | _response_timer = millis(); 62 | 63 | if (_response_retries == 0) { 64 | waiting_for_response = false; 65 | disconnect_handler(NULL); 66 | } else { 67 | send_message(); 68 | } 69 | 70 | --_response_retries; 71 | } 72 | } 73 | 74 | return waiting_for_response; 75 | } 76 | 77 | bool MQTTSN::connected() { 78 | return _connected; 79 | } 80 | 81 | uint16_t MQTTSN::bswap(const uint16_t val) { 82 | return (val << 8) | (val >> 8); 83 | } 84 | 85 | uint16_t MQTTSN::find_topic_id(const char* name, uint8_t* index) { 86 | for (uint8_t i = 0; i < topic_count; ++i) { 87 | if (strcmp(topic_table[i].name, name) == 0 && topic_table[i].id != 0xffff) { 88 | *index = i; 89 | return topic_table[i].id; 90 | } 91 | } 92 | 93 | return 0xffff; 94 | } 95 | 96 | #ifdef USE_SERIAL 97 | void MQTTSN::parse_stream(uint8_t* buf, uint16_t len) { 98 | memcpy(response_buffer, (const void*)buf, len); 99 | dispatch(); 100 | } 101 | #endif 102 | 103 | #ifdef USE_RF12 104 | void MQTTSN::parse_rf12() { 105 | memcpy(response_buffer, (const void*)rf12_data, RF12_MAXDATA < MAX_BUFFER_SIZE ? RF12_MAXDATA : MAX_BUFFER_SIZE); 106 | dispatch(); 107 | } 108 | #endif 109 | 110 | void MQTTSN::dispatch() { 111 | message_header* response_message = (message_header*)response_buffer; 112 | 113 | switch (response_message->type) { 114 | case ADVERTISE: 115 | advertise_handler((msg_advertise*)response_buffer); 116 | break; 117 | 118 | case GWINFO: 119 | gwinfo_handler((msg_gwinfo*)response_buffer); 120 | break; 121 | 122 | case CONNACK: 123 | connack_handler((msg_connack*)response_buffer); 124 | break; 125 | 126 | case WILLTOPICREQ: 127 | willtopicreq_handler(response_message); 128 | break; 129 | 130 | case WILLMSGREQ: 131 | willmsgreq_handler(response_message); 132 | break; 133 | 134 | case REGISTER: 135 | register_handler((msg_register*)response_buffer); 136 | break; 137 | 138 | case REGACK: 139 | regack_handler((msg_regack*)response_buffer); 140 | break; 141 | 142 | case PUBLISH: 143 | publish_handler((msg_publish*)response_buffer); 144 | break; 145 | 146 | case PUBACK: 147 | puback_handler((msg_puback*)response_buffer); 148 | break; 149 | 150 | case SUBACK: 151 | suback_handler((msg_suback*)response_buffer); 152 | break; 153 | 154 | case UNSUBACK: 155 | unsuback_handler((msg_unsuback*)response_buffer); 156 | break; 157 | 158 | case PINGREQ: 159 | pingreq_handler((msg_pingreq*)response_buffer); 160 | break; 161 | 162 | case PINGRESP: 163 | pingresp_handler(); 164 | break; 165 | 166 | case DISCONNECT: 167 | disconnect_handler((msg_disconnect*)response_buffer); 168 | break; 169 | 170 | case WILLTOPICRESP: 171 | willtopicresp_handler((msg_willtopicresp*)response_buffer); 172 | break; 173 | 174 | case WILLMSGRESP: 175 | willmsgresp_handler((msg_willmsgresp*)response_buffer); 176 | break; 177 | 178 | default: 179 | return; 180 | } 181 | 182 | waiting_for_response = false; 183 | } 184 | 185 | void MQTTSN::send_message() { 186 | message_header* hdr = reinterpret_cast(message_buffer); 187 | 188 | #ifdef USE_RF12 189 | while (!rf12_canSend()) { 190 | rf12_recvDone(); 191 | Sleepy::loseSomeTime(32); 192 | } 193 | rf12_sendStart(_gateway_id, message_buffer, hdr->length); 194 | rf12_sendWait(2); 195 | #endif 196 | #ifdef USE_SERIAL 197 | extern void MQTTSN_serial_send(uint8_t* message_buffer, int length); 198 | MQTTSN_serial_send(message_buffer, hdr->length); 199 | #endif 200 | 201 | if (!waiting_for_response) { 202 | _response_timer = millis(); 203 | _response_retries = N_RETRY; 204 | } 205 | } 206 | 207 | void MQTTSN::advertise_handler(const msg_advertise* msg) { 208 | _gateway_id = msg->gw_id; 209 | } 210 | 211 | extern void MQTTSN_gwinfo_handler(const msg_gwinfo* msg); 212 | void MQTTSN::gwinfo_handler(const msg_gwinfo* msg) { 213 | MQTTSN_gwinfo_handler(msg); 214 | } 215 | 216 | void MQTTSN::connack_handler(const msg_connack* msg) { 217 | _connected = 1; 218 | } 219 | 220 | void MQTTSN::willtopicreq_handler(const message_header* msg) { 221 | } 222 | 223 | void MQTTSN::willmsgreq_handler(const message_header* msg) { 224 | } 225 | 226 | void MQTTSN::regack_handler(const msg_regack* msg) { 227 | if (msg->return_code == 0 && topic_count < MAX_TOPICS && bswap(msg->message_id) == _message_id) { 228 | topic_table[topic_count].id = bswap(msg->topic_id); 229 | ++topic_count; 230 | } 231 | } 232 | 233 | void MQTTSN::puback_handler(const msg_puback* msg) { 234 | } 235 | 236 | #ifdef USE_QOS2 237 | void MQTTSN::pubrec_handler(const msg_pubqos2* msg) { 238 | } 239 | 240 | void MQTTSN::pubrel_handler(const msg_pubqos2* msg) { 241 | } 242 | 243 | void MQTTSN::pubcomp_handler(const msg_pubqos2* msg) { 244 | } 245 | #endif 246 | 247 | void MQTTSN::pingreq_handler(const msg_pingreq* msg) { 248 | pingresp(); 249 | } 250 | 251 | void MQTTSN::suback_handler(const msg_suback* msg) { 252 | } 253 | 254 | void MQTTSN::unsuback_handler(const msg_unsuback* msg) { 255 | } 256 | 257 | void MQTTSN::disconnect_handler(const msg_disconnect* msg) { 258 | _connected = false; 259 | } 260 | 261 | void MQTTSN::pingresp_handler() { 262 | } 263 | 264 | extern void MQTTSN_publish_handler(const msg_publish* msg); 265 | void MQTTSN::publish_handler(const msg_publish* msg) { 266 | if (msg->flags & FLAG_QOS_1) { 267 | return_code_t ret = REJECTED_INVALID_TOPIC_ID; 268 | const uint16_t topic_id = bswap(msg->topic_id); 269 | 270 | for (uint8_t i = 0; i < topic_count; ++i) { 271 | if (topic_table[i].id == topic_id) { 272 | ret = ACCEPTED; 273 | MQTTSN_publish_handler(msg); 274 | break; 275 | } 276 | } 277 | 278 | puback(msg->topic_id, msg->message_id, ret); 279 | } 280 | } 281 | 282 | void MQTTSN::register_handler(const msg_register* msg) { 283 | return_code_t ret = REJECTED_INVALID_TOPIC_ID; 284 | uint8_t index; 285 | uint16_t topic_id = find_topic_id(msg->topic_name, &index); 286 | 287 | if (topic_id != 0xffff) { 288 | topic_table[index].id = bswap(msg->topic_id); 289 | ret = ACCEPTED; 290 | } 291 | 292 | regack(msg->topic_id, msg->message_id, ret); 293 | } 294 | 295 | void MQTTSN::willtopicresp_handler(const msg_willtopicresp* msg) { 296 | } 297 | 298 | void MQTTSN::willmsgresp_handler(const msg_willmsgresp* msg) { 299 | } 300 | 301 | void MQTTSN::searchgw(const uint8_t radius) { 302 | msg_searchgw* msg = reinterpret_cast(message_buffer); 303 | 304 | msg->length = sizeof(msg_searchgw); 305 | msg->type = SEARCHGW; 306 | msg->radius = radius; 307 | 308 | send_message(); 309 | waiting_for_response = true; 310 | } 311 | 312 | void MQTTSN::connect(const uint8_t flags, const uint16_t duration, const char* client_id) { 313 | msg_connect* msg = reinterpret_cast(message_buffer); 314 | 315 | msg->length = sizeof(msg_connect) + strlen(client_id); 316 | msg->type = CONNECT; 317 | msg->flags = flags; 318 | msg->protocol_id = PROTOCOL_ID; 319 | msg->duration = bswap(duration); 320 | strcpy(msg->client_id, client_id); 321 | 322 | send_message(); 323 | _connected = false; 324 | waiting_for_response = true; 325 | } 326 | 327 | void MQTTSN::willtopic(const uint8_t flags, const char* will_topic, const bool update) { 328 | if (will_topic == NULL) { 329 | message_header* msg = reinterpret_cast(message_buffer); 330 | 331 | msg->type = update ? WILLTOPICUPD : WILLTOPIC; 332 | msg->length = sizeof(message_header); 333 | } else { 334 | msg_willtopic* msg = reinterpret_cast(message_buffer); 335 | 336 | msg->type = update ? WILLTOPICUPD : WILLTOPIC; 337 | msg->flags = flags; 338 | strcpy(msg->will_topic, will_topic); 339 | } 340 | 341 | send_message(); 342 | 343 | if ((flags & QOS_MASK) == FLAG_QOS_1 || (flags & QOS_MASK) == FLAG_QOS_2) { 344 | waiting_for_response = true; 345 | } 346 | } 347 | 348 | void MQTTSN::willmsg(const void* will_msg, const uint8_t will_msg_len, const bool update) { 349 | msg_willmsg* msg = reinterpret_cast(message_buffer); 350 | 351 | msg->length = sizeof(msg_willmsg) + will_msg_len; 352 | msg->type = update ? WILLMSGUPD : WILLMSG; 353 | memcpy(msg->willmsg, will_msg, will_msg_len); 354 | 355 | send_message(); 356 | } 357 | 358 | void MQTTSN::disconnect(const uint16_t duration) { 359 | msg_disconnect* msg = reinterpret_cast(message_buffer); 360 | 361 | msg->length = sizeof(message_header); 362 | msg->type = DISCONNECT; 363 | 364 | if (duration > 0) { 365 | msg->length += sizeof(duration); 366 | msg->duration = bswap(duration); 367 | } 368 | 369 | send_message(); 370 | waiting_for_response = true; 371 | } 372 | 373 | bool MQTTSN::register_topic(const char* name) { 374 | if (!waiting_for_response && topic_count < (MAX_TOPICS - 1)) { 375 | ++_message_id; 376 | 377 | // Fill in the next table entry, but we only increment the counter to 378 | // the next topic when we get a REGACK from the broker. So don't issue 379 | // another REGISTER until we have resolved this one. 380 | topic_table[topic_count].name = name; 381 | topic_table[topic_count].id = 0xffff; 382 | 383 | msg_register* msg = reinterpret_cast(message_buffer); 384 | 385 | msg->length = sizeof(msg_register) + strlen(name); 386 | msg->type = REGISTER; 387 | msg->topic_id = 0; 388 | msg->message_id = bswap(_message_id); 389 | strcpy(msg->topic_name, name); 390 | 391 | send_message(); 392 | waiting_for_response = true; 393 | return true; 394 | } 395 | 396 | return false; 397 | } 398 | 399 | void MQTTSN::regack(const uint16_t topic_id, const uint16_t message_id, const return_code_t return_code) { 400 | msg_regack* msg = reinterpret_cast(message_buffer); 401 | 402 | msg->length = sizeof(msg_regack); 403 | msg->type = REGACK; 404 | msg->topic_id = bswap(topic_id); 405 | msg->message_id = bswap(message_id); 406 | msg->return_code = return_code; 407 | 408 | send_message(); 409 | } 410 | 411 | void MQTTSN::publish(const uint8_t flags, const uint16_t topic_id, const void* data, const uint8_t data_len) { 412 | ++_message_id; 413 | 414 | msg_publish* msg = reinterpret_cast(message_buffer); 415 | 416 | msg->length = sizeof(msg_publish) + data_len; 417 | msg->type = PUBLISH; 418 | msg->flags = flags; 419 | msg->topic_id = bswap(topic_id); 420 | msg->message_id = bswap(_message_id); 421 | memcpy(msg->data, data, data_len); 422 | 423 | send_message(); 424 | 425 | if ((flags & QOS_MASK) == FLAG_QOS_1 || (flags & QOS_MASK) == FLAG_QOS_2) { 426 | waiting_for_response = true; 427 | } 428 | } 429 | 430 | #ifdef USE_QOS2 431 | void MQTTSN::pubrec() { 432 | msg_pubqos2* msg = reinterpret_cast(message_buffer); 433 | msg->length = sizeof(msg_pubqos2); 434 | msg->type = PUBREC; 435 | msg->message_id = bswap(_message_id); 436 | 437 | send_message(); 438 | } 439 | 440 | void MQTTSN::pubrel() { 441 | msg_pubqos2* msg = reinterpret_cast(message_buffer); 442 | msg->length = sizeof(msg_pubqos2); 443 | msg->type = PUBREL; 444 | msg->message_id = bswap(_message_id); 445 | 446 | send_message(); 447 | } 448 | 449 | void MQTTSN::pubcomp() { 450 | msg_pubqos2* msg = reinterpret_cast(message_buffer); 451 | msg->length = sizeof(msg_pubqos2); 452 | msg->type = PUBCOMP; 453 | msg->message_id = bswap(_message_id); 454 | 455 | send_message(); 456 | } 457 | #endif 458 | 459 | void MQTTSN::puback(const uint16_t topic_id, const uint16_t message_id, const return_code_t return_code) { 460 | msg_puback* msg = reinterpret_cast(message_buffer); 461 | 462 | msg->length = sizeof(msg_puback); 463 | msg->type = PUBACK; 464 | msg->topic_id = bswap(topic_id); 465 | msg->message_id = bswap(message_id); 466 | msg->return_code = return_code; 467 | 468 | send_message(); 469 | } 470 | 471 | void MQTTSN::subscribe_by_name(const uint8_t flags, const char* topic_name) { 472 | ++_message_id; 473 | 474 | msg_subscribe* msg = reinterpret_cast(message_buffer); 475 | 476 | // The -2 here is because we're unioning a 0-length member (topic_name) 477 | // with a uint16_t in the msg_subscribe struct. 478 | msg->length = sizeof(msg_subscribe) + strlen(topic_name) - 2; 479 | msg->type = SUBSCRIBE; 480 | msg->flags = (flags & QOS_MASK) | FLAG_TOPIC_NAME; 481 | msg->message_id = bswap(_message_id); 482 | strcpy(msg->topic_name, topic_name); 483 | 484 | send_message(); 485 | 486 | if ((flags & QOS_MASK) == FLAG_QOS_1 || (flags & QOS_MASK) == FLAG_QOS_2) { 487 | waiting_for_response = true; 488 | } 489 | } 490 | 491 | void MQTTSN::subscribe_by_id(const uint8_t flags, const uint16_t topic_id) { 492 | ++_message_id; 493 | 494 | msg_subscribe* msg = reinterpret_cast(message_buffer); 495 | 496 | msg->length = sizeof(msg_subscribe); 497 | msg->type = SUBSCRIBE; 498 | msg->flags = (flags & QOS_MASK) | FLAG_TOPIC_PREDEFINED_ID; 499 | msg->message_id = bswap(_message_id); 500 | msg->topic_id = bswap(topic_id); 501 | 502 | send_message(); 503 | 504 | if ((flags & QOS_MASK) == FLAG_QOS_1 || (flags & QOS_MASK) == FLAG_QOS_2) { 505 | waiting_for_response = true; 506 | } 507 | } 508 | 509 | void MQTTSN::unsubscribe_by_name(const uint8_t flags, const char* topic_name) { 510 | ++_message_id; 511 | 512 | msg_unsubscribe* msg = reinterpret_cast(message_buffer); 513 | 514 | // The -2 here is because we're unioning a 0-length member (topic_name) 515 | // with a uint16_t in the msg_unsubscribe struct. 516 | msg->length = sizeof(msg_unsubscribe) + strlen(topic_name) - 2; 517 | msg->type = UNSUBSCRIBE; 518 | msg->flags = (flags & QOS_MASK) | FLAG_TOPIC_NAME; 519 | msg->message_id = bswap(_message_id); 520 | strcpy(msg->topic_name, topic_name); 521 | 522 | send_message(); 523 | 524 | if ((flags & QOS_MASK) == FLAG_QOS_1 || (flags & QOS_MASK) == FLAG_QOS_2) { 525 | waiting_for_response = true; 526 | } 527 | } 528 | 529 | void MQTTSN::unsubscribe_by_id(const uint8_t flags, const uint16_t topic_id) { 530 | ++_message_id; 531 | 532 | msg_unsubscribe* msg = reinterpret_cast(message_buffer); 533 | 534 | msg->length = sizeof(msg_unsubscribe); 535 | msg->type = UNSUBSCRIBE; 536 | msg->flags = (flags & QOS_MASK) | FLAG_TOPIC_PREDEFINED_ID; 537 | msg->message_id = bswap(_message_id); 538 | msg->topic_id = bswap(topic_id); 539 | 540 | send_message(); 541 | 542 | if ((flags & QOS_MASK) == FLAG_QOS_1 || (flags & QOS_MASK) == FLAG_QOS_2) { 543 | waiting_for_response = true; 544 | } 545 | } 546 | 547 | void MQTTSN::pingreq(const char* client_id) { 548 | msg_pingreq* msg = reinterpret_cast(message_buffer); 549 | msg->length = sizeof(msg_pingreq) + strlen(client_id); 550 | msg->type = PINGREQ; 551 | strcpy(msg->client_id, client_id); 552 | 553 | send_message(); 554 | 555 | waiting_for_response = true; 556 | } 557 | 558 | void MQTTSN::pingresp() { 559 | message_header* msg = reinterpret_cast(message_buffer); 560 | msg->length = sizeof(message_header); 561 | msg->type = PINGRESP; 562 | 563 | send_message(); 564 | } 565 | -------------------------------------------------------------------------------- /Arduino/libraries/mqttsn/mqttsn-messages.h: -------------------------------------------------------------------------------- 1 | /* 2 | mqttsn-messages.h 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (C) 2014 John Donovan 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | */ 26 | 27 | #ifndef __MQTTSN_MESSAGES_H__ 28 | #define __MQTTSN_MESSAGES_H__ 29 | 30 | #include "mqttsn.h" 31 | 32 | #define MAX_TOPICS 10 33 | #define MAX_BUFFER_SIZE 66 34 | 35 | class MQTTSN { 36 | public: 37 | MQTTSN(); 38 | virtual ~MQTTSN(); 39 | 40 | uint16_t find_topic_id(const char* name, uint8_t* index); 41 | bool wait_for_response(); 42 | bool connected(); 43 | #ifdef USE_SERIAL 44 | void parse_stream(uint8_t* buf, uint16_t len); 45 | #endif 46 | #ifdef USE_RF12 47 | void parse_rf12(); 48 | #endif 49 | 50 | void searchgw(const uint8_t radius); 51 | void connect(const uint8_t flags, const uint16_t duration, const char* client_id); 52 | void willtopic(const uint8_t flags, const char* will_topic, const bool update = false); 53 | void willmsg(const void* will_msg, const uint8_t will_msg_len, const bool update = false); 54 | bool register_topic(const char* name); 55 | void publish(const uint8_t flags, const uint16_t topic_id, const void* data, const uint8_t data_len); 56 | #ifdef USE_QOS2 57 | void pubrec(); 58 | void pubrel(); 59 | void pubcomp(); 60 | #endif 61 | void subscribe_by_name(const uint8_t flags, const char* topic_name); 62 | void subscribe_by_id(const uint8_t flags, const uint16_t topic_id); 63 | void unsubscribe_by_name(const uint8_t flags, const char* topic_name); 64 | void unsubscribe_by_id(const uint8_t flags, const uint16_t topic_id); 65 | void pingreq(const char* client_id); 66 | void pingresp(); 67 | void disconnect(const uint16_t duration); 68 | 69 | protected: 70 | virtual void advertise_handler(const msg_advertise* msg); 71 | virtual void gwinfo_handler(const msg_gwinfo* msg); 72 | virtual void connack_handler(const msg_connack* msg); 73 | virtual void willtopicreq_handler(const message_header* msg); 74 | virtual void willmsgreq_handler(const message_header* msg); 75 | virtual void regack_handler(const msg_regack* msg); 76 | virtual void publish_handler(const msg_publish* msg); 77 | virtual void register_handler(const msg_register* msg); 78 | virtual void puback_handler(const msg_puback* msg); 79 | #ifdef USE_QOS2 80 | virtual void pubrec_handler(const msg_pubqos2* msg); 81 | virtual void pubrel_handler(const msg_pubqos2* msg); 82 | virtual void pubcomp_handler(const msg_pubqos2* msg); 83 | #endif 84 | virtual void suback_handler(const msg_suback* msg); 85 | virtual void unsuback_handler(const msg_unsuback* msg); 86 | virtual void pingreq_handler(const msg_pingreq* msg); 87 | virtual void pingresp_handler(); 88 | virtual void disconnect_handler(const msg_disconnect* msg); 89 | virtual void willtopicresp_handler(const msg_willtopicresp* msg); 90 | virtual void willmsgresp_handler(const msg_willmsgresp* msg); 91 | 92 | void regack(const uint16_t topic_id, const uint16_t message_id, const return_code_t return_code); 93 | void puback(const uint16_t topic_id, const uint16_t message_id, const return_code_t return_code); 94 | 95 | private: 96 | struct topic { 97 | const char* name; 98 | uint16_t id; 99 | }; 100 | 101 | void dispatch(); 102 | uint16_t bswap(const uint16_t val); 103 | void send_message(); 104 | 105 | // Set to true when we're waiting for some sort of acknowledgement from the 106 | //server that will transition our state. 107 | bool waiting_for_response; 108 | bool _connected; 109 | uint16_t _message_id; 110 | uint8_t topic_count; 111 | 112 | uint8_t message_buffer[MAX_BUFFER_SIZE]; 113 | uint8_t response_buffer[MAX_BUFFER_SIZE]; 114 | topic topic_table[MAX_TOPICS]; 115 | 116 | uint8_t _gateway_id; 117 | uint32_t _response_timer; 118 | uint8_t _response_retries; 119 | }; 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /Arduino/libraries/mqttsn/mqttsn.h: -------------------------------------------------------------------------------- 1 | /* 2 | mqttsn.h 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (C) 2014 John Donovan 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | */ 26 | 27 | #ifndef __MQTTSN_H__ 28 | #define __MQTTSN_H__ 29 | 30 | //#define USE_RF12 1 31 | #define USE_SERIAL 1 32 | 33 | #define PROTOCOL_ID 0x01 34 | 35 | #define FLAG_DUP 0x80 36 | #define FLAG_QOS_0 0x00 37 | #define FLAG_QOS_1 0x20 38 | #define FLAG_QOS_2 0x40 39 | #define FLAG_QOS_M1 0x60 40 | #define FLAG_RETAIN 0x10 41 | #define FLAG_WILL 0x08 42 | #define FLAG_CLEAN 0x04 43 | #define FLAG_TOPIC_NAME 0x00 44 | #define FLAG_TOPIC_PREDEFINED_ID 0x01 45 | #define FLAG_TOPIC_SHORT_NAME 0x02 46 | 47 | #define QOS_MASK (FLAG_QOS_0 | FLAG_QOS_1 | FLAG_QOS_2 | FLAG_QOS_M1) 48 | #define TOPIC_MASK (FLAG_TOPIC_NAME | FLAG_TOPIC_PREDEFINED_ID | FLAG_TOPIC_SHORT_NAME) 49 | 50 | // Recommended values for timers and counters. All timers are in seconds. 51 | #define T_ADV 960 52 | #define N_ADV 3 53 | #define T_SEARCH_GW 5 54 | #define T_GW_INFO 5 55 | #define T_WAIT 360 56 | #define T_RETRY 15 57 | #define N_RETRY 5 58 | 59 | enum return_code_t { 60 | ACCEPTED, 61 | REJECTED_CONGESTION, 62 | REJECTED_INVALID_TOPIC_ID, 63 | REJECTED_NOT_SUPPORTED 64 | }; 65 | 66 | enum message_type { 67 | ADVERTISE, 68 | SEARCHGW, 69 | GWINFO, 70 | CONNECT = 0x04, 71 | CONNACK, 72 | WILLTOPICREQ, 73 | WILLTOPIC, 74 | WILLMSGREQ, 75 | WILLMSG, 76 | REGISTER, 77 | REGACK, 78 | PUBLISH, 79 | PUBACK, 80 | PUBCOMP, 81 | PUBREC, 82 | PUBREL, 83 | SUBSCRIBE = 0x12, 84 | SUBACK, 85 | UNSUBSCRIBE, 86 | UNSUBACK, 87 | PINGREQ, 88 | PINGRESP, 89 | DISCONNECT, 90 | WILLTOPICUPD = 0x1a, 91 | WILLTOPICRESP, 92 | WILLMSGUPD, 93 | WILLMSGRESP 94 | }; 95 | 96 | struct message_header { 97 | uint8_t length; 98 | uint8_t type; 99 | }; 100 | 101 | struct msg_advertise : public message_header { 102 | uint8_t gw_id; 103 | uint16_t duration; 104 | }; 105 | 106 | struct msg_searchgw : public message_header { 107 | uint8_t radius; 108 | }; 109 | 110 | struct msg_gwinfo : public message_header { 111 | uint8_t gw_id; 112 | char gw_add[0]; 113 | }; 114 | 115 | struct msg_connect : public message_header { 116 | uint8_t flags; 117 | uint8_t protocol_id; 118 | uint16_t duration; 119 | char client_id[0]; 120 | }; 121 | 122 | struct msg_connack : public message_header { 123 | return_code_t return_code; 124 | }; 125 | 126 | struct msg_willtopic : public message_header { 127 | uint8_t flags; 128 | char will_topic[0]; 129 | }; 130 | 131 | struct msg_willmsg : public message_header { 132 | char willmsg[0]; 133 | }; 134 | 135 | struct msg_register : public message_header { 136 | uint16_t topic_id; 137 | uint16_t message_id; 138 | char topic_name[0]; 139 | }; 140 | 141 | struct msg_regack : public message_header { 142 | uint16_t topic_id; 143 | uint16_t message_id; 144 | uint8_t return_code; 145 | }; 146 | 147 | struct msg_publish : public message_header { 148 | uint8_t flags; 149 | uint16_t topic_id; 150 | uint16_t message_id; 151 | char data[0]; 152 | }; 153 | 154 | struct msg_puback : public message_header { 155 | uint16_t topic_id; 156 | uint16_t message_id; 157 | uint8_t return_code; 158 | }; 159 | 160 | struct msg_pubqos2 : public message_header { 161 | uint16_t message_id; 162 | }; 163 | 164 | struct msg_subscribe : public message_header { 165 | uint8_t flags; 166 | uint16_t message_id; 167 | union { 168 | char topic_name[0]; 169 | uint16_t topic_id; 170 | }; 171 | }; 172 | 173 | struct msg_suback : public message_header { 174 | uint8_t flags; 175 | uint16_t topic_id; 176 | uint16_t message_id; 177 | uint8_t return_code; 178 | }; 179 | 180 | struct msg_unsubscribe : public message_header { 181 | uint8_t flags; 182 | uint16_t message_id; 183 | union { 184 | char topic_name[0]; 185 | uint16_t topic_id; 186 | }; 187 | }; 188 | 189 | struct msg_unsuback : public message_header { 190 | uint16_t message_id; 191 | }; 192 | 193 | struct msg_pingreq : public message_header { 194 | char client_id[0]; 195 | }; 196 | 197 | struct msg_disconnect : public message_header { 198 | uint16_t duration; 199 | }; 200 | 201 | struct msg_willtopicresp : public message_header { 202 | uint8_t return_code; 203 | }; 204 | 205 | struct msg_willmsgresp : public message_header { 206 | uint8_t return_code; 207 | }; 208 | 209 | #endif 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MQTT-SN-Arduino 2 | =============== 3 | 4 | TODO: I don't like the repo name anymore, change it to something more generic 5 | 6 | Connecting mesh network to the MQTT broker and tunneling MQTT protocol over Websocket. 7 | -------------- 8 | 9 | Mesh network is based on the open-source MeshBee modules with Arduino end devices and Raspberry Pi acting as MQTT mesh network gateway. 10 | TODO: Add links to MQTT, etc 11 | 12 | Overall project architecture: 13 | TODO: Awesome diagrams should be here 14 | 15 | There are multiple pieces of the puzzle: 16 | - Arduino/libraries/mqttsn/folder. This is an MQTT-SN Arduino library, fork of theh http://bitbucket.org/MerseyViking/mqtt-sn-arduino with some minor bug fixes (TODO: get in touch with the repo owner to merge). Copy mqttsn folder into your Arduino libraries folder. 17 | - Arduino/MqttsnClient folder is an Arduino test sketch (publishes Arduino temperature to /arduino/temp topic). There is no frame logic, for now assuming direct/transparent serial connection to the broker. 18 | - "serial-mqtts" folder. Serial to MQTT-SN/UDP gateway. Dummy simple Python script to read MQTT-SN from serial port and send it as UDP packet. 19 | - RSMB Broker. Compiled version and configuration file of the "Really Small Message Broker" from http://git.eclipse.org/c/mosquitto/org.eclipse.mosquitto.rsmb.git 20 | - Websocket to TCP gateway. This is slightly modified "websockify" project from https://github.com/kanaka/websockify to tunnel MQTT over Websocket. Compatible with http://mqtt.io client. 21 | 22 | TODO: 23 | 1. Test! 24 | 2. Create PLC MQTT client. 25 | -------------------------------------------------------------------------------- /RSMB/Messages.1.3.0.2: -------------------------------------------------------------------------------- 1 | #//%W% %I% 2 | #/******************************************************************************* 3 | #* Copyright (c) 2007, 2013 IBM Corp. 4 | #* 5 | #* All rights reserved. This program and the accompanying materials 6 | #* are made available under the terms of the Eclipse Public License v1.0 7 | #* and Eclipse Distribution License v1.0 which accompany this distribution. 8 | #* 9 | #* The Eclipse Public License is available at 10 | #* http://www.eclipse.org/legal/epl-v10.html 11 | #* and the Eclipse Distribution License is available at 12 | #* http://www.eclipse.org/org/documents/edl-v10.php. 13 | #* 14 | #* Contributors: 15 | #* Ian Craggs - initial API and implementation and/or initial documentation 16 | #*******************************************************************************/ 17 | # NLS_MESSAGEFORMAT_VAR 18 | # NLS_ENCODING=UNICODE 19 | 0=Could not read configuration file %s 20 | 1=No value for keyword %s on line %d 21 | 2=Unrecognized topic direction %s 22 | 3=Setting property "%s" to %s 23 | 4=Unrecognized boolean value %s on line number %d 24 | 5=Setting property "%s" to "%s" 25 | 6=Adding value "%s" to list "%s" 26 | 7=Setting property "%s" to %d 27 | 8=Unrecognized configuration keyword %s on line number %d 28 | 9=Cannot open %s file %s for writing, %ss will not be saved 29 | 10=Cannot open %s file %s for reading, %ss will not be restored 30 | 11=Restoring %ss from file %s 31 | 12=Wildcard in publish topic %.20s from client %s not allowed 32 | 13=Internal error; FFDC written to file %s 33 | 14=MQTT protocol starting, listening on port %d 34 | 15=Cannot start listener on port %d 35 | 16=MQTT protocol stopping 36 | 17=Closing client %s 37 | 18=Socket error for client identifier %s, socket %d, peer address %s; ending connection 38 | 19=Bad packet for client identifier %s, socket %d, peer address %s; ending connection 39 | 20=Socket error on socket %d, peer address %s; ending connection 40 | 21=Badly formed packet on socket %d, peer address %s; ending connection 41 | 22=Unknown MQTT packet type %d on socket %d 42 | 23=Connect was not first packet on socket %d, peer address %s; got %s 43 | 24=%d second keepalive timeout for client %s, ending connection 44 | 25=Incorrect configuration: acl_file requires password_file to be specified 45 | 28=Trying PUBLISH again for client %s, socket %d, message identifier %d 46 | 29=Socket error for client %s, socket %d, ending connection 47 | 30=Trying PUBREL again for client %s, message identifier %d 48 | 31=Refusing connection attempt for unauthorized client identifier %s 49 | 32=Connection attempt using unsupported protocol %s version %d received 50 | 33=Connection attempt to listener %d received from client %s on address %s 51 | 34=Duplicate connection attempt received for client identifier "%s" from address %s, ending oldest connection 52 | 38=Disconnection request received from client %s 53 | 39=Invalid user entry on line number %d 54 | 40=Unrecognized user %s on line number %d 55 | 41=Invalid access control rule on line number %d 56 | 42=Uptime: %d seconds 57 | 43=Messages received: %d 58 | 44=Messages sent: %d 59 | 45=Queued message limit reached for client %s. Number of messages discarded so far: %d 60 | 46=Broker stopping 61 | 47=Broker stopped 62 | 48=First Failure Data Capture (FFDC) condition occurred, but FFDC writing turned off 63 | 49=Configuration file name is %s 64 | 50=Packet %s received from client %s for message identifier %d, but no record of that message identifier found 65 | 51=Packet %s received from client %s for message identifier %d, but message is wrong QoS, %d 66 | 52=Packet %s received from client %s for message identifier %d, but message is in wrong state 67 | 53=Version %s, %s 68 | 54=Features included: 69 | 55=Maximum heap use: %d bytes 70 | 56=Bridge connection %s not started because its client identifier %s is a duplicate 71 | 57=Connection %s is deleted 72 | 58=Error deleting connection %s 73 | 59=No bridge connection with name %s 74 | 60=Stopping bridge connection %s 75 | 61=Unable to stop connection %s 76 | 62=Connection %s is now stopped 77 | 63=Stopping connection %s on idle timeout 78 | 64=%d queued messages discarded for client %s 79 | 65=Unable to clear retained flag for system topic %s 80 | 66=Discarding retained message with invalid topic name %s 81 | 67=Error getting network format for address %s 82 | 68=Processing command file %s 83 | 71=Unexpected ping response received for client %s 84 | 75=Socket error %d (%s) in %s for socket %d 85 | 77=Cannot open socket 86 | 78=Cannot bind port %d 87 | 79=TCP listen call failed for port %d 88 | 92=%s is not a valid IP address 89 | 99=Trying bridge connection %s address %s again, without private protocol 90 | 100=Autosaving persistent data after %d changes 91 | 101=Autosaving persistent data after %d second interval 92 | 104=Saving persistence state at user request 93 | 105=Ignoring user request to save state because there are no changes 94 | 109=Failed to setsockopt SO_REUSEADDR on listening port %d 95 | 123=Trying backup bridge connection %s 96 | 124=Starting bridge connection %s 97 | 125=Error starting bridge connection 98 | 127=Starting reconnection attempt for bridge connection %s, address %s 99 | 128=TCP connect timeout for bridge connection %s 100 | 129=Can't get connection completion state from socket 101 | 130=Connect for bridge client %s, address %s, failed with TCP error code %d 102 | 132=Connection acknowledgement rc %d received for client %s 103 | 133=Bridge connection %s to %s now established 104 | 134=Closing bridge backup connection %s 105 | 135=Inbound bridge topic %s does not match any pattern for connection %s 106 | 136=Outbound bridge topic %s does not match any pattern for connection %s 107 | 139=retained message 108 | 140=subscription 109 | 141=Refusing connection attempt by client %s; maximum number of connections %d already reached for listener %d 110 | 142=Incorrect configuration: no addresses for bridge connection %s 111 | 143=Ping response not received within %d seconds for bridge connection %s; ending connection 112 | 144=Connection with name %s already exists; add failed 113 | 145=Message queue for client %s is more than %d%% full 114 | 146=Message queue for client %s is less than %d%% full 115 | 147=Error saving retained message persistence file 116 | 148=Error saving subscription persistence file 117 | 149=Client %s is not authorized to publish to topic: %s 118 | 150=Client %s is not authorized to subscribe to topic: %s 119 | 151=Cannot give read access to topic: %s 120 | 152=Unrecognized configuration value %s on line number %d 121 | 153=Invalid topic syntax in subscription %.20s from client identifier %s, peer address %s 122 | 300=MQTT-S protocol starting, listening on port %d 123 | 301=MQTT-S protocol stopping 124 | 302=Unknown interface %s for if_nametoindex 125 | 303=Adding multicast interface %s on interface %s 126 | -------------------------------------------------------------------------------- /RSMB/broker_mqtts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boriz/MQTT-SN-Arduino/b4c49c7cc66fcf377f3c0121eded152afdcfeddc/RSMB/broker_mqtts -------------------------------------------------------------------------------- /RSMB/standalone.conf: -------------------------------------------------------------------------------- 1 | # will show you packets being sent and received 2 | trace_output protocol 3 | 4 | # normal MQTT listener 5 | listener 1883 INADDR_ANY 6 | 7 | # MQTT-S listener 8 | listener 1883 INADDR_ANY mqtts 9 | 10 | -------------------------------------------------------------------------------- /S7-1200/MQTT_Demo_V14.zap14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boriz/MQTT-SN-Arduino/b4c49c7cc66fcf377f3c0121eded152afdcfeddc/S7-1200/MQTT_Demo_V14.zap14 -------------------------------------------------------------------------------- /serial-mqtts/MB_PacketTest.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import serial 3 | import sys 4 | import time 5 | import datetime 6 | from struct import * 7 | 8 | #SerPort = '/dev/ttyAMA0' 9 | SerPort = 'COM19' 10 | DEST_ADDR = 0x0000 11 | 12 | # From firmware_at_api.h 13 | API_DATA_LEN = 20 14 | AT_PARAM_LEN = 8 15 | API_START_DELIMITER = 0x7E 16 | 17 | OPTION_CAST_MASK = 0x40 #option unicast or broadcast MASK 18 | OPTION_ACK_MASK = 0x80 #option ACK or not MASK 19 | 20 | API_REMOTE_AT_REQ = 0x17 21 | API_DATA_PACKET = 0x02 22 | API_TX_REQ = 0x01 #Tx a packet to special short address 23 | API_TX_RESP = 0x03 24 | API_RX_PACKET = 0x81 #received a packet from air,send to UART 25 | 26 | ATIO = 0x70 27 | 28 | #convert string to hex 29 | def ToHex(s): 30 | retHex="" 31 | for c in s: 32 | retHex = retHex + "%#04x " % c 33 | return retHex 34 | 35 | 36 | # Connect to serial SerPort 37 | def ConnectSer (): 38 | try: 39 | print "Connecting to Serial:"+SerPort+" ... ", 40 | ser = serial.Serial(SerPort, 115200, timeout=1) 41 | ser.flush() 42 | print "Connected" 43 | return ser 44 | except: 45 | print "Failed to connect to serial port" 46 | raise SystemExit 47 | 48 | 49 | # Create TX data 50 | def CreateTx (data): 51 | # data array 52 | dat = bytearray(data) 53 | 54 | # payload data (tsTxDataPacket) 55 | pay = bytearray() 56 | pay.append (pack('>B', 1)) # frame id 57 | pay.append (pack('>B', 0)) # option 58 | pay.append (pack('>B', len(dat))) # data length 59 | pay = pay + dat # add data 60 | for s in range (API_DATA_LEN - len(dat)): # fill the rest of the data with zeros 61 | pay.append (pack('>B', 0)) 62 | pay = pay + bytearray(unpack('2B', pack('>H', DEST_ADDR))) # Unicast address (16 bits) 63 | 64 | # Create a packet (tsApiSpec) 65 | rep = bytearray() 66 | rep.append(pack('>B', API_START_DELIMITER)) # Delimiter 67 | rep.append(pack('>B', len(pay))) # Length of the payload 68 | rep.append(pack('>B', API_DATA_PACKET)) # API ID 69 | rep = rep + pay 70 | cs = sum(pay) & 0xFF #checksum 71 | rep.append(pack('>B', cs)) # Checksum (payload only, sum of all bytes) 72 | return rep 73 | 74 | 75 | # Create At frame 76 | def CreateAtIO (par): 77 | # data array 78 | p = bytearray(par) 79 | 80 | # payload data (tsTxDataPacket) 81 | pay = bytearray() 82 | pay.append (pack('>B', 1)) # frame id 83 | pay.append (pack('>B', 0)) # option 84 | pay.append (pack('>B', ATIO)) # ATIO command 85 | pay = pay + p # add data (AtIO command parameters) 86 | for s in range (AT_PARAM_LEN - len(p)): # fill the rest of the parameters with zeros 87 | pay.append (pack('>B', 0)) 88 | pay = pay + bytearray(unpack('2B', pack('>H', DEST_ADDR))) # Unicast address (16 bits) 89 | 90 | # Create a packet (tsApiSpec) 91 | rep = bytearray() 92 | rep.append(pack('>B', API_START_DELIMITER)) # Delimiter 93 | rep.append(pack('>B', len(pay))) # Length of the payload 94 | rep.append(pack('>B', API_REMOTE_AT_REQ)) # API ID 95 | rep = rep + pay 96 | cs = sum(pay) & 0xFF #checksum 97 | rep.append(pack('>B', cs)) # Checksum (payload only, sum of all bytes) 98 | return rep 99 | 100 | 101 | # Main program 102 | # Create API_TX_REQ packet 103 | s = CreateTx('Test') 104 | #s = CreateAtIO('\x09\x01') # IO pin for LED9, turn it on 105 | 106 | # Print it 107 | print "Packet:" + ToHex(s) 108 | 109 | # Send over serial 110 | print "Sending Serial" 111 | ser = ConnectSer() 112 | ser.write(s) 113 | ser.flush() 114 | if ser: 115 | ser.close() 116 | print "Done" 117 | 118 | -------------------------------------------------------------------------------- /serial-mqtts/ser_redir.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import serial 3 | import sys 4 | import time 5 | import datetime 6 | from struct import * 7 | 8 | #SerPort = '/dev/ttyAMA0' 9 | SerPort = 'COM30' 10 | #BrokerHost = "192.168.1.10" 11 | BrokerHost = "mqtt.dmcinfo.com" 12 | BrokerPort = 1883 13 | 14 | API_DATA_LEN = 20 15 | API_DATA_PACKET = 0x02 16 | API_START_DELIMITER = 0x7E 17 | 18 | #convert string to hex 19 | def ToHex(s): 20 | retHex="" 21 | for c in s: 22 | if type(c) is int: 23 | retHex = retHex + "%#04x " % c 24 | else: 25 | retHex = retHex + "%#04x " % ord(c) 26 | return retHex 27 | 28 | #connect to serial SerPort 29 | def ConnectSer (): 30 | try: 31 | print "Connecting to Serial:"+SerPort+" ... ", 32 | ser = serial.Serial(SerPort, 115200, timeout=1) 33 | ser.write("ATAP\n") 34 | ser.flush() 35 | time.sleep(0.1) 36 | ser.flushInput() 37 | print "Connected" 38 | return ser 39 | except: 40 | print "Failed to connect to serial port" 41 | raise SystemExit 42 | 43 | # Class to hold connection information 44 | class conn: 45 | def __init__(self): 46 | self.sock 47 | self.src_addr 48 | self.frame_id 49 | 50 | 51 | # Create TX data 52 | def CreateTx (dest_addr, data): 53 | # data array 54 | dat = bytearray(data) 55 | 56 | # payload data (tsTxDataPacket) 57 | pay = bytearray() 58 | pay.append (pack('>B', 1)) # frame id 59 | pay.append (pack('>B', 0)) # option 60 | pay.append (pack('>B', len(dat))) # data length 61 | pay = pay + dat # add data 62 | for s in range (API_DATA_LEN - len(dat)): # fill the rest of the data with zeros 63 | pay.append (pack('>B', 0)) 64 | pay = pay + bytearray(unpack('2B', pack('>H', dest_addr))) # Unicast address (16 bits) 65 | 66 | # Create a packet (tsApiSpec) 67 | rep = bytearray() 68 | rep.append(pack('>B', API_START_DELIMITER)) # Delimiter 69 | rep.append(pack('>B', len(pay))) # Length of the payload 70 | rep.append(pack('>B', API_DATA_PACKET)) # API ID 71 | rep = rep + pay 72 | cs = sum(pay) & 0xFF #checksum 73 | rep.append(pack('>B', cs)) # Checksum (payload only, sum of all bytes) 74 | return rep 75 | 76 | 77 | # Parse RX data 78 | def ParseRx (ser_data): 79 | # data array 80 | dat = bytearray(ser_data) 81 | 82 | try: 83 | # Header 84 | i=0 85 | delimiter = dat[i] # Delimeter 86 | #print "delimiter: " + str(delimiter) 87 | i += 1 88 | pay_len = dat[i] # Payload length 89 | #print "pay_len: " + str(pay_len) 90 | i += 1 91 | api = dat[i] # API ID 92 | i += 1 93 | 94 | # payload data (tsTxDataPacket) 95 | fid = dat[i] # frame id 96 | i += 1 97 | opt = dat[i] # option 98 | i += 1 99 | dat_len = dat[i] # data length 100 | #print "dat_len: " + str(dat_len) 101 | i += 1 102 | d_start = i 103 | d = bytearray() # Data 104 | for s in range (dat_len): 105 | d.append(dat[i]) 106 | i += 1 107 | #print "pay: " + str(i) + " = " + str (dat[i]) 108 | 109 | # Grab source address (data start + data length) 110 | i = d_start + API_DATA_LEN 111 | src = dat[i] * 256 + dat[i+1] 112 | #print "src: " + str(src) 113 | return (src, d) 114 | 115 | except: 116 | return (None, None) 117 | 118 | 119 | # Main loop 120 | data_ser = "" 121 | data_udp = "" 122 | conns = [] 123 | host_ip = socket.gethostbyname(BrokerHost) 124 | ser = ConnectSer() 125 | while True: 126 | try: 127 | # Try to get serial data 128 | if ser.inWaiting() > 0: 129 | data_ser = ser.read(1) 130 | if ord(data_ser[0]) != 0x7E: 131 | # Should start with 0x7E 132 | print "Wrong frame delimiter:%#04x" % ord(data_ser[0]) 133 | ser.flushInput() 134 | data_ser = "" 135 | time.sleep(0.1) 136 | continue 137 | 138 | # Get the rest of the packet 139 | print "Frame start detected" 140 | while ser.inWaiting() > 0: 141 | while ser.inWaiting() > 0: 142 | data_ser = data_ser + ser.read(1) 143 | time.sleep(0.05) 144 | 145 | # Got serial data 146 | if data_ser: 147 | l = len(data_ser) 148 | print "SER Got frame:" + ToHex(data_ser) + "; len:" + str(l) 149 | 150 | # Parse the frame 151 | src = None 152 | payload = None 153 | (src, payload) = ParseRx(data_ser) 154 | 155 | # Clear serial data 156 | data_ser="" 157 | 158 | # Valid data? 159 | if src <>None and payload<>None: 160 | # Valid data ! 161 | print "SER Source addr:" + hex(src) 162 | print "SER Payload:", 163 | for s in payload: 164 | print hex(s), 165 | print 166 | 167 | # Try to find a corresponding connection by source address 168 | cn = None 169 | for i in conns: 170 | if i.src_addr == src: 171 | cn = i 172 | print "SER Found source address:" + "%#04x" % cn.src_addr 173 | 174 | # Found record? 175 | if not cn: 176 | # Nope, create a new record 177 | cn = conn 178 | cn.src_addr = src 179 | cn.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 180 | cn.sock.bind(('',0)) 181 | cn.sock.setblocking(0) 182 | cn.frame_id = 1 183 | conns.append(cn) 184 | print "SER Create new connection, Src:" + "%#04x" % cn.src_addr + " Port:" + str(cn.sock.getsockname()[1]) 185 | 186 | # send serial data to UDP 187 | cn.sock.sendto(payload, (host_ip, BrokerPort)) 188 | print "SER Sent to UDP" 189 | else: 190 | print "SER Wrong frame format" 191 | 192 | # Check for incoming UDP data 193 | for i in conns: 194 | try: 195 | data_udp = i.sock.recv(256) 196 | except: 197 | data_udp="" 198 | 199 | # Got udp data for client 200 | if data_udp: 201 | print "UDP input for address:" + "%#04x" % i.src_addr 202 | print "UDP payload:" + ToHex(data_udp) 203 | rep = CreateTx(i.src_addr, data_udp) 204 | ser.write(rep) 205 | ser.flush() 206 | print "UDP sent to serial:" + ToHex(rep) 207 | time.sleep(0.05) 208 | 209 | except KeyboardInterrupt: 210 | print "CTRL-C" 211 | break 212 | 213 | 214 | print "Closing Serial and UDP" 215 | ser.close() 216 | for i in conns: 217 | if i.sock: 218 | i.sock.close() 219 | print "Closed" 220 | 221 | -------------------------------------------------------------------------------- /subscribe.py: -------------------------------------------------------------------------------- 1 | import paho.mqtt.client as mosquitto 2 | 3 | def on_connect(mosq, obj, rc): 4 | # mosq.subscribe("arduino/temp", 0) 5 | print("Connect. rc: "+str(rc)) 6 | 7 | def on_message(mosq, obj, msg): 8 | print("Message. Top:"+msg.topic+"; Qos:"+str(msg.qos)+"; Payload:"+str(msg.payload)) 9 | 10 | def on_publish(mosq, obj, mid): 11 | print("Publish. Mid: "+str(mid)) 12 | 13 | def on_subscribe(mosq, obj, mid, granted_qos): 14 | print("Subscribed: Mid:"+str(mid)+"; Qos:"+str(granted_qos)) 15 | 16 | def on_log(mosq, obj, level, string): 17 | print(string) 18 | 19 | # If you want to use a specific client id, use 20 | # mqttc = mosquitto.Mosquitto("client-id") 21 | # but note that the client id must be unique on the broker. Leaving the client 22 | # id parameter empty will generate a random id for you. 23 | mqttc = mosquitto.Mosquitto() 24 | mqttc.on_message = on_message 25 | mqttc.on_connect = on_connect 26 | mqttc.on_publish = on_publish 27 | mqttc.on_subscribe = on_subscribe 28 | # Uncomment to enable debug messages 29 | mqttc.on_log = on_log 30 | 31 | mqttc.connect("192.168.1.10", 1883, 60) 32 | #mqttc.connect("127.0.0.1", 1883, 60) 33 | 34 | mqttc.subscribe("#", 0) 35 | #mqttc.subscribe(("tuple", 1)) 36 | #mqttc.subscribe([("list0", 0), ("list1", 1)]) 37 | 38 | mqttc.loop_forever() -------------------------------------------------------------------------------- /websocket-mqtt/Makefile: -------------------------------------------------------------------------------- 1 | TARGETS=websockify 2 | CFLAGS += -fPIC 3 | 4 | all: $(TARGETS) 5 | 6 | websockify: websockify.o websocket.o 7 | $(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@ 8 | 9 | websocket.o: websocket.c websocket.h 10 | websockify.o: websockify.c websocket.h 11 | 12 | clean: 13 | rm -f websockify *.o 14 | 15 | -------------------------------------------------------------------------------- /websocket-mqtt/websocket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * WebSocket lib with support for "wss://" encryption. 3 | * Copyright 2010 Joel Martin 4 | * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) 5 | * 6 | * You can make a cert/key with openssl using: 7 | * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem 8 | * as taken from http://docs.python.org/dev/library/ssl.html#certificates 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include // daemonizing 21 | #include // daemonizing 22 | #include 23 | #include 24 | #include /* base64 encode/decode */ 25 | #include /* md5 hash */ 26 | #include /* sha1 hash */ 27 | #include "websocket.h" 28 | 29 | /* 30 | * Global state 31 | * 32 | * Warning: not thread safe 33 | */ 34 | int ssl_initialized = 0; 35 | int pipe_error = 0; 36 | settings_t settings; 37 | 38 | 39 | void traffic(char * token) { 40 | if ((settings.verbose) && (! settings.daemon)) { 41 | fprintf(stdout, "%s", token); 42 | fflush(stdout); 43 | } 44 | } 45 | 46 | void error(char *msg) 47 | { 48 | perror(msg); 49 | } 50 | 51 | void fatal(char *msg) 52 | { 53 | perror(msg); 54 | exit(1); 55 | } 56 | 57 | /* resolve host with also IP address parsing */ 58 | int resolve_host(struct in_addr *sin_addr, const char *hostname) 59 | { 60 | if (!inet_aton(hostname, sin_addr)) { 61 | struct addrinfo *ai, *cur; 62 | struct addrinfo hints; 63 | memset(&hints, 0, sizeof(hints)); 64 | hints.ai_family = AF_INET; 65 | if (getaddrinfo(hostname, NULL, &hints, &ai)) 66 | return -1; 67 | for (cur = ai; cur; cur = cur->ai_next) { 68 | if (cur->ai_family == AF_INET) { 69 | *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr; 70 | freeaddrinfo(ai); 71 | return 0; 72 | } 73 | } 74 | freeaddrinfo(ai); 75 | return -1; 76 | } 77 | return 0; 78 | } 79 | 80 | 81 | /* 82 | * SSL Wrapper Code 83 | */ 84 | 85 | ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len) { 86 | if (ctx->ssl) { 87 | //handler_msg("SSL recv\n"); 88 | return SSL_read(ctx->ssl, buf, len); 89 | } else { 90 | return recv(ctx->sockfd, buf, len, 0); 91 | } 92 | } 93 | 94 | ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) { 95 | if (ctx->ssl) { 96 | //handler_msg("SSL send\n"); 97 | return SSL_write(ctx->ssl, buf, len); 98 | } else { 99 | return send(ctx->sockfd, buf, len, 0); 100 | } 101 | } 102 | 103 | ws_ctx_t *alloc_ws_ctx() { 104 | ws_ctx_t *ctx; 105 | if (! (ctx = malloc(sizeof(ws_ctx_t))) ) 106 | { fatal("malloc()"); } 107 | 108 | if (! (ctx->cin_buf = malloc(BUFSIZE)) ) 109 | { fatal("malloc of cin_buf"); } 110 | if (! (ctx->cout_buf = malloc(BUFSIZE)) ) 111 | { fatal("malloc of cout_buf"); } 112 | if (! (ctx->tin_buf = malloc(BUFSIZE)) ) 113 | { fatal("malloc of tin_buf"); } 114 | if (! (ctx->tout_buf = malloc(BUFSIZE)) ) 115 | { fatal("malloc of tout_buf"); } 116 | 117 | ctx->headers = malloc(sizeof(headers_t)); 118 | ctx->ssl = NULL; 119 | ctx->ssl_ctx = NULL; 120 | return ctx; 121 | } 122 | 123 | int free_ws_ctx(ws_ctx_t *ctx) { 124 | free(ctx->cin_buf); 125 | free(ctx->cout_buf); 126 | free(ctx->tin_buf); 127 | free(ctx->tout_buf); 128 | free(ctx); 129 | } 130 | 131 | ws_ctx_t *ws_socket(ws_ctx_t *ctx, int socket) { 132 | ctx->sockfd = socket; 133 | } 134 | 135 | ws_ctx_t *ws_socket_ssl(ws_ctx_t *ctx, int socket, char * certfile, char * keyfile) { 136 | int ret; 137 | char msg[1024]; 138 | char * use_keyfile; 139 | ws_socket(ctx, socket); 140 | 141 | if (keyfile && (keyfile[0] != '\0')) { 142 | // Separate key file 143 | use_keyfile = keyfile; 144 | } else { 145 | // Combined key and cert file 146 | use_keyfile = certfile; 147 | } 148 | 149 | // Initialize the library 150 | if (! ssl_initialized) { 151 | SSL_library_init(); 152 | OpenSSL_add_all_algorithms(); 153 | SSL_load_error_strings(); 154 | ssl_initialized = 1; 155 | 156 | } 157 | 158 | ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method()); 159 | if (ctx->ssl_ctx == NULL) { 160 | ERR_print_errors_fp(stderr); 161 | fatal("Failed to configure SSL context"); 162 | } 163 | 164 | if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, use_keyfile, 165 | SSL_FILETYPE_PEM) <= 0) { 166 | sprintf(msg, "Unable to load private key file %s\n", use_keyfile); 167 | fatal(msg); 168 | } 169 | 170 | if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, certfile, 171 | SSL_FILETYPE_PEM) <= 0) { 172 | sprintf(msg, "Unable to load certificate file %s\n", certfile); 173 | fatal(msg); 174 | } 175 | 176 | // if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, "DEFAULT") != 1) { 177 | // sprintf(msg, "Unable to set cipher\n"); 178 | // fatal(msg); 179 | // } 180 | 181 | // Associate socket and ssl object 182 | ctx->ssl = SSL_new(ctx->ssl_ctx); 183 | SSL_set_fd(ctx->ssl, socket); 184 | 185 | ret = SSL_accept(ctx->ssl); 186 | if (ret < 0) { 187 | ERR_print_errors_fp(stderr); 188 | return NULL; 189 | } 190 | 191 | return ctx; 192 | } 193 | 194 | int ws_socket_free(ws_ctx_t *ctx) { 195 | if (ctx->ssl) { 196 | SSL_free(ctx->ssl); 197 | ctx->ssl = NULL; 198 | } 199 | if (ctx->ssl_ctx) { 200 | SSL_CTX_free(ctx->ssl_ctx); 201 | ctx->ssl_ctx = NULL; 202 | } 203 | if (ctx->sockfd) { 204 | shutdown(ctx->sockfd, SHUT_RDWR); 205 | close(ctx->sockfd); 206 | ctx->sockfd = 0; 207 | } 208 | } 209 | 210 | /* ------------------------------------------------------- */ 211 | 212 | int encode_mqtt(u_char const *src, size_t srclength, char *target, size_t targsize, unsigned int opcode) 213 | { 214 | unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0; 215 | 216 | if ((int)srclength <= 0) 217 | { 218 | return 0; 219 | } 220 | 221 | b64_sz = srclength; 222 | #ifdef DEBUG 223 | printf("Mask new frame, len=%u\n", b64_sz); 224 | #endif 225 | target[0] = (char)(opcode & 0x0F | 0x80); 226 | 227 | if (b64_sz <= 125) { 228 | target[1] = (char) b64_sz; 229 | payload_offset = 2; 230 | } else if ((b64_sz > 125) && (b64_sz < 65536)) { 231 | target[1] = (char) 126; 232 | *(u_short*)&(target[2]) = htons(b64_sz); 233 | payload_offset = 4; 234 | } else { 235 | handler_emsg("Sending frames larger than 65535 bytes not supported\n"); 236 | return -1; 237 | } 238 | #ifdef DEBUG 239 | printf("frame[0..3]: 0x%x 0x%x 0x%x 0x%x (pay off: %d)\n", 240 | (unsigned char) target[0], 241 | (unsigned char) target[1], 242 | (unsigned char) target[2], 243 | (unsigned char) target[3], payload_offset); 244 | #endif 245 | memcpy (target+payload_offset, src, srclength); 246 | return srclength + payload_offset; 247 | } 248 | 249 | 250 | int decode_mqtt(unsigned char *src, size_t srclength, unsigned char *target, size_t targsize, int *opcode, unsigned int *left) 251 | { 252 | unsigned char *frame, *mask, *payload, save_char, cntstr[4];; 253 | int masked = 0; 254 | int i = 0, len, framecount = 0; 255 | size_t remaining; 256 | unsigned int target_offset = 0, hdr_length = 0, payload_length = 0; 257 | 258 | *left = srclength; 259 | frame = src; 260 | #ifdef DEBUG 261 | printf("Unmask new frame\n"); 262 | #endif 263 | while (1) { 264 | // Need at least two bytes of the header 265 | // Find beginning of next frame. First time hdr_length, masked and 266 | // payload_length are zero 267 | frame += hdr_length + 4*masked + payload_length; 268 | #ifdef DEBUG 269 | printf("frame[0..3]: 0x%x 0x%x 0x%x 0x%x (tot: %d)\n", 270 | (unsigned char) frame[0], 271 | (unsigned char) frame[1], 272 | (unsigned char) frame[2], 273 | (unsigned char) frame[3], srclength); 274 | #endif 275 | if (frame > src + srclength) { 276 | printf("Truncated frame from client, need %d more bytes\n", frame - (src + srclength) ); 277 | break; 278 | } 279 | remaining = (src + srclength) - frame; 280 | if (remaining < 2) { 281 | printf("Truncated frame header from client\n"); 282 | break; 283 | } 284 | framecount ++; 285 | 286 | *opcode = frame[0] & 0x0f; 287 | masked = (frame[1] & 0x80) >> 7; 288 | 289 | if (*opcode == 0x8) { 290 | // client sent orderly close frame 291 | break; 292 | } 293 | 294 | payload_length = frame[1] & 0x7f; 295 | if (payload_length < 126) { 296 | hdr_length = 2; 297 | //frame += 2 * sizeof(char); 298 | } else if (payload_length == 126) { 299 | payload_length = (frame[2] << 8) + frame[3]; 300 | hdr_length = 4; 301 | } else { 302 | handler_emsg("Receiving frames larger than 65535 bytes not supported\n"); 303 | return -1; 304 | } 305 | if ((hdr_length + 4*masked + payload_length) > remaining) { 306 | continue; 307 | } 308 | #ifdef DEBUG 309 | printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining); 310 | #endif 311 | payload = frame + hdr_length + 4*masked; 312 | 313 | if (*opcode != 1 && *opcode != 2) { 314 | handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode); 315 | continue; 316 | } 317 | 318 | if (payload_length == 0) { 319 | handler_msg("Ignoring empty frame\n"); 320 | continue; 321 | } 322 | 323 | if ((payload_length > 0) && (!masked)) { 324 | handler_emsg("Received unmasked payload from client\n"); 325 | return -1; 326 | } 327 | 328 | // Terminate with a null for base64 decode 329 | save_char = payload[payload_length]; 330 | payload[payload_length] = '\0'; 331 | 332 | // unmask the data 333 | mask = payload - 4; 334 | for (i = 0; i < payload_length; i++) { 335 | payload[i] ^= mask[i%4]; 336 | } 337 | 338 | // Copy frame to the output 339 | memcpy(target+target_offset, payload, payload_length); 340 | target_offset += payload_length; 341 | #ifdef DEBUG 342 | printf(" len %d\n", payload_length); 343 | #endif 344 | } 345 | 346 | if (framecount > 1) { 347 | snprintf(cntstr, 3, "%d", framecount); 348 | traffic(cntstr); 349 | } 350 | 351 | *left = remaining; 352 | return target_offset; 353 | } 354 | 355 | 356 | 357 | int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) { 358 | char *start, *end; 359 | headers_t *headers = ws_ctx->headers; 360 | 361 | headers->key1[0] = '\0'; 362 | headers->key2[0] = '\0'; 363 | headers->key3[0] = '\0'; 364 | 365 | if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0)) { 366 | return 0; 367 | } 368 | start = handshake+4; 369 | end = strstr(start, " HTTP/1.1"); 370 | if (!end) { return 0; } 371 | strncpy(headers->path, start, end-start); 372 | headers->path[end-start] = '\0'; 373 | 374 | start = strstr(handshake, "\r\nHost: "); 375 | if (!start) { return 0; } 376 | start += 8; 377 | end = strstr(start, "\r\n"); 378 | strncpy(headers->host, start, end-start); 379 | headers->host[end-start] = '\0'; 380 | 381 | headers->origin[0] = '\0'; 382 | start = strstr(handshake, "\r\nOrigin: "); 383 | if (start) { 384 | start += 10; 385 | } else { 386 | start = strstr(handshake, "\r\nSec-WebSocket-Origin: "); 387 | if (!start) { return 0; } 388 | start += 24; 389 | } 390 | end = strstr(start, "\r\n"); 391 | strncpy(headers->origin, start, end-start); 392 | headers->origin[end-start] = '\0'; 393 | 394 | start = strstr(handshake, "\r\nSec-WebSocket-Version: "); 395 | if (start) { 396 | // RFC 6455 397 | start += 25; 398 | end = strstr(start, "\r\n"); 399 | strncpy(headers->version, start, end-start); 400 | headers->version[end-start] = '\0'; 401 | start = strstr(handshake, "\r\nSec-WebSocket-Key: "); 402 | if (!start) { return 0; } 403 | start += 21; 404 | end = strstr(start, "\r\n"); 405 | strncpy(headers->key1, start, end-start); 406 | headers->key1[end-start] = '\0'; 407 | 408 | start = strstr(handshake, "\r\nConnection: "); 409 | if (!start) { return 0; } 410 | start += 14; 411 | end = strstr(start, "\r\n"); 412 | strncpy(headers->connection, start, end-start); 413 | headers->connection[end-start] = '\0'; 414 | 415 | start = strstr(handshake, "\r\nSec-WebSocket-Protocol: "); 416 | if (!start) { return 0; } 417 | start += 26; 418 | end = strstr(start, "\r\n"); 419 | strncpy(headers->protocols, start, end-start); 420 | headers->protocols[end-start] = '\0'; 421 | } 422 | else 423 | { 424 | handler_msg("Protocol is not supported (only RFC6455 is supported)\n"); 425 | } 426 | 427 | return 1; 428 | } 429 | 430 | static void gen_sha1(headers_t *headers, char *target) { 431 | SHA_CTX c; 432 | unsigned char hash[SHA_DIGEST_LENGTH]; 433 | int r; 434 | 435 | SHA1_Init(&c); 436 | SHA1_Update(&c, headers->key1, strlen(headers->key1)); 437 | SHA1_Update(&c, "EpfvMdcoQCUdHswgSBh9", 20); 438 | SHA1_Final(hash, &c); 439 | 440 | r = b64_ntop(hash, sizeof hash, target, 29); 441 | } 442 | 443 | 444 | ws_ctx_t *do_handshake(int sock) { 445 | char handshake[4096], response[4096], sha1[29], trailer[17]; 446 | char *scheme, *pre; 447 | headers_t *headers; 448 | int len, ret, i, offset; 449 | ws_ctx_t * ws_ctx; 450 | 451 | // Peek, but don't read the data 452 | len = recv(sock, handshake, 1024, MSG_PEEK); 453 | handshake[len] = 0; 454 | if (len == 0) { 455 | handler_msg("ignoring empty handshake\n"); 456 | return NULL; 457 | } else if (bcmp(handshake, "", 22) == 0) { 458 | len = recv(sock, handshake, 1024, 0); 459 | handshake[len] = 0; 460 | handler_msg("sending flash policy response\n"); 461 | send(sock, POLICY_RESPONSE, sizeof(POLICY_RESPONSE), 0); 462 | return NULL; 463 | } else if ((bcmp(handshake, "\x16", 1) == 0) || 464 | (bcmp(handshake, "\x80", 1) == 0)) { 465 | // SSL 466 | if (!settings.cert) { 467 | handler_msg("SSL connection but no cert specified\n"); 468 | return NULL; 469 | } else if (access(settings.cert, R_OK) != 0) { 470 | handler_msg("SSL connection but '%s' not found\n", 471 | settings.cert); 472 | return NULL; 473 | } 474 | ws_ctx = alloc_ws_ctx(); 475 | ws_socket_ssl(ws_ctx, sock, settings.cert, settings.key); 476 | if (! ws_ctx) { return NULL; } 477 | scheme = "wss"; 478 | handler_msg("using SSL socket\n"); 479 | } else if (settings.ssl_only) { 480 | handler_msg("non-SSL connection disallowed\n"); 481 | return NULL; 482 | } else { 483 | ws_ctx = alloc_ws_ctx(); 484 | ws_socket(ws_ctx, sock); 485 | if (! ws_ctx) { return NULL; } 486 | scheme = "ws"; 487 | handler_msg("using plain (not SSL) socket\n"); 488 | } 489 | offset = 0; 490 | for (i = 0; i < 10; i++) { 491 | len = ws_recv(ws_ctx, handshake+offset, 4096); 492 | if (len == 0) { 493 | handler_emsg("Client closed during handshake\n"); 494 | return NULL; 495 | } 496 | offset += len; 497 | handshake[offset] = 0; 498 | if (strstr(handshake, "\r\n\r\n")) { 499 | break; 500 | } 501 | usleep(10); 502 | } 503 | 504 | handler_msg("handshake: %s\n", handshake); 505 | if (!parse_handshake(ws_ctx, handshake)) { 506 | handler_emsg("Invalid WS request\n"); 507 | return NULL; 508 | } 509 | 510 | headers = ws_ctx->headers; 511 | if (strcasecmp(headers->protocols, "mqtt") != 0) 512 | { 513 | handler_msg("MQTT protocol detected\n"); 514 | gen_sha1(headers, sha1); 515 | sprintf(response, SERVER_HANDSHAKE, sha1, headers->protocols); 516 | } 517 | else 518 | { 519 | handler_msg("Unsupported protocol: %s\n", headers->protocols); 520 | } 521 | 522 | handler_msg("response: %s\n", response); 523 | ws_send(ws_ctx, response, strlen(response)); 524 | 525 | return ws_ctx; 526 | } 527 | 528 | void signal_handler(sig) { 529 | switch (sig) { 530 | case SIGHUP: break; // ignore for now 531 | case SIGPIPE: pipe_error = 1; break; // handle inline 532 | case SIGTERM: exit(0); break; 533 | } 534 | } 535 | 536 | void daemonize(int keepfd) { 537 | int pid, i; 538 | 539 | umask(0); 540 | chdir("/"); 541 | setgid(getgid()); 542 | setuid(getuid()); 543 | 544 | /* Double fork to daemonize */ 545 | pid = fork(); 546 | if (pid<0) { fatal("fork error"); } 547 | if (pid>0) { exit(0); } // parent exits 548 | setsid(); // Obtain new process group 549 | pid = fork(); 550 | if (pid<0) { fatal("fork error"); } 551 | if (pid>0) { exit(0); } // parent exits 552 | 553 | /* Signal handling */ 554 | signal(SIGHUP, signal_handler); // catch HUP 555 | signal(SIGTERM, signal_handler); // catch kill 556 | 557 | /* Close open files */ 558 | for (i=getdtablesize(); i>=0; --i) { 559 | if (i != keepfd) { 560 | close(i); 561 | } else if (settings.verbose) { 562 | printf("keeping fd %d\n", keepfd); 563 | } 564 | } 565 | i=open("/dev/null", O_RDWR); // Redirect stdin 566 | dup(i); // Redirect stdout 567 | dup(i); // Redirect stderr 568 | } 569 | 570 | 571 | void start_server() { 572 | int lsock, csock, pid, clilen, sopt = 1, i; 573 | struct sockaddr_in serv_addr, cli_addr; 574 | ws_ctx_t *ws_ctx; 575 | 576 | 577 | /* Initialize buffers */ 578 | lsock = socket(AF_INET, SOCK_STREAM, 0); 579 | if (lsock < 0) 580 | { 581 | error("ERROR creating listener socket"); 582 | } 583 | bzero((char *) &serv_addr, sizeof(serv_addr)); 584 | serv_addr.sin_family = AF_INET; 585 | serv_addr.sin_port = htons(settings.listen_port); 586 | 587 | /* Resolve listen address */ 588 | if (settings.listen_host && (settings.listen_host[0] != '\0')) { 589 | if (resolve_host(&serv_addr.sin_addr, settings.listen_host) < -1) { 590 | fatal("Could not resolve listen address"); 591 | } 592 | } else { 593 | serv_addr.sin_addr.s_addr = INADDR_ANY; 594 | } 595 | 596 | setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *)&sopt, sizeof(sopt)); 597 | if (bind(lsock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 598 | fatal("ERROR on binding listener socket"); 599 | } 600 | listen(lsock,100); 601 | 602 | signal(SIGPIPE, signal_handler); // catch pipe 603 | 604 | if (settings.daemon) { 605 | daemonize(lsock); 606 | } 607 | 608 | 609 | // Reep zombies 610 | signal(SIGCHLD, SIG_IGN); 611 | 612 | printf("Waiting for connections on %s:%d\n", settings.listen_host, settings.listen_port); 613 | 614 | while (1) { 615 | clilen = sizeof(cli_addr); 616 | pipe_error = 0; 617 | pid = 0; 618 | csock = accept(lsock, 619 | (struct sockaddr *) &cli_addr, 620 | &clilen); 621 | if (csock < 0) { 622 | error("ERROR on accept"); 623 | continue; 624 | } 625 | handler_msg("got client connection from %s\n", 626 | inet_ntoa(cli_addr.sin_addr)); 627 | 628 | if (!settings.run_once) { 629 | handler_msg("forking handler process\n"); 630 | pid = fork(); 631 | } 632 | 633 | if (pid == 0) { // handler process 634 | ws_ctx = do_handshake(csock); 635 | if (settings.run_once) { 636 | if (ws_ctx == NULL) { 637 | // Not a real WebSocket connection 638 | continue; 639 | } else { 640 | // Successful connection, stop listening for new 641 | // connections 642 | close(lsock); 643 | } 644 | } 645 | if (ws_ctx == NULL) { 646 | handler_msg("No connection after handshake\n"); 647 | break; // Child process exits 648 | } 649 | 650 | settings.handler(ws_ctx); 651 | if (pipe_error) { 652 | handler_emsg("Closing due to SIGPIPE\n"); 653 | } 654 | break; // Child process exits 655 | } else { // parent process 656 | settings.handler_id += 1; 657 | } 658 | } 659 | if (pid == 0) { 660 | if (ws_ctx) { 661 | ws_socket_free(ws_ctx); 662 | free_ws_ctx(ws_ctx); 663 | } else { 664 | shutdown(csock, SHUT_RDWR); 665 | close(csock); 666 | } 667 | handler_msg("handler exit\n"); 668 | } else { 669 | handler_msg("websockify exit\n"); 670 | } 671 | 672 | } 673 | 674 | -------------------------------------------------------------------------------- /websocket-mqtt/websocket.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BUFSIZE 65536 4 | #define DBUFSIZE (BUFSIZE * 3) / 4 - 20 5 | 6 | #define SERVER_HANDSHAKE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ 7 | Upgrade: WebSocket\r\n\ 8 | Connection: Upgrade\r\n\ 9 | %sWebSocket-Origin: %s\r\n\ 10 | %sWebSocket-Location: %s://%s%s\r\n\ 11 | %sWebSocket-Protocol: %s\r\n\ 12 | \r\n%s" 13 | 14 | #define POLICY_RESPONSE "\n" 15 | 16 | typedef struct { 17 | char path[1024+1]; 18 | char host[1024+1]; 19 | char origin[1024+1]; 20 | char version[1024+1]; 21 | char connection[1024+1]; 22 | char protocols[1024+1]; 23 | char key1[1024+1]; 24 | char key2[1024+1]; 25 | char key3[8+1]; 26 | } headers_t; 27 | 28 | typedef struct { 29 | int sockfd; 30 | SSL_CTX *ssl_ctx; 31 | SSL *ssl; 32 | headers_t *headers; 33 | char *cin_buf; 34 | char *cout_buf; 35 | char *tin_buf; 36 | char *tout_buf; 37 | } ws_ctx_t; 38 | 39 | typedef struct { 40 | int verbose; 41 | char listen_host[256]; 42 | int listen_port; 43 | void (*handler)(ws_ctx_t*); 44 | int handler_id; 45 | char *cert; 46 | char *key; 47 | int ssl_only; 48 | int daemon; 49 | int run_once; 50 | } settings_t; 51 | 52 | 53 | ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len); 54 | 55 | ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len); 56 | 57 | /* base64.c declarations */ 58 | //int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize); 59 | //int b64_pton(char const *src, u_char *target, size_t targsize); 60 | 61 | #define gen_handler_msg(stream, ...) \ 62 | if (! settings.daemon) { \ 63 | fprintf(stream, " %d: ", settings.handler_id); \ 64 | fprintf(stream, __VA_ARGS__); \ 65 | } 66 | 67 | #define handler_msg(...) gen_handler_msg(stdout, __VA_ARGS__); 68 | #define handler_emsg(...) gen_handler_msg(stderr, __VA_ARGS__); 69 | 70 | -------------------------------------------------------------------------------- /websocket-mqtt/websockify: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boriz/MQTT-SN-Arduino/b4c49c7cc66fcf377f3c0121eded152afdcfeddc/websocket-mqtt/websockify -------------------------------------------------------------------------------- /websocket-mqtt/websockify.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A WebSocket to TCP socket proxy with support for "wss://" encryption. 3 | * Copyright 2010 Joel Martin 4 | * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) 5 | * 6 | * You can make a cert/key with openssl using: 7 | * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem 8 | * as taken from http://docs.python.org/dev/library/ssl.html#certificates 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "websocket.h" 21 | 22 | char traffic_legend[] = "\n\ 23 | Traffic Legend:\n\ 24 | } - Client receive\n\ 25 | }. - Client receive partial\n\ 26 | { - Target receive\n\ 27 | \n\ 28 | > - Target send\n\ 29 | >. - Target send partial\n\ 30 | < - Client send\n\ 31 | <. - Client send partial\n\ 32 | "; 33 | 34 | char USAGE[] = "Usage: [options] " \ 35 | "[source_addr:]source_port target_addr:target_port\n\n" \ 36 | " --verbose|-v verbose messages and per frame traffic\n" \ 37 | " --daemon|-D become a daemon (background process)\n" \ 38 | " --cert CERT SSL certificate file\n" \ 39 | " --key KEY SSL key file (if separate from cert)\n" \ 40 | " --ssl-only disallow non-encrypted connections"; 41 | 42 | #define usage(fmt, args...) \ 43 | fprintf(stderr, "%s\n\n", USAGE); \ 44 | fprintf(stderr, fmt , ## args); \ 45 | exit(1); 46 | 47 | char target_host[256]; 48 | int target_port; 49 | 50 | extern pipe_error; 51 | extern settings_t settings; 52 | 53 | void do_proxy(ws_ctx_t *ws_ctx, int target) { 54 | fd_set rlist, wlist, elist; 55 | struct timeval tv; 56 | int i, maxfd, client = ws_ctx->sockfd; 57 | unsigned int opcode, left, ret; 58 | unsigned int tout_start, tout_end, cout_start, cout_end; 59 | unsigned int tin_start, tin_end; 60 | ssize_t len, bytes; 61 | 62 | tout_start = tout_end = cout_start = cout_end; 63 | tin_start = tin_end = 0; 64 | maxfd = client > target ? client+1 : target+1; 65 | 66 | while (1) { 67 | tv.tv_sec = 1; 68 | tv.tv_usec = 0; 69 | 70 | FD_ZERO(&rlist); 71 | FD_ZERO(&wlist); 72 | FD_ZERO(&elist); 73 | 74 | FD_SET(client, &elist); 75 | FD_SET(target, &elist); 76 | 77 | if (tout_end == tout_start) { 78 | // Nothing queued for target, so read from client 79 | FD_SET(client, &rlist); 80 | } else { 81 | // Data queued for target, so write to it 82 | FD_SET(target, &wlist); 83 | } 84 | if (cout_end == cout_start) { 85 | // Nothing queued for client, so read from target 86 | FD_SET(target, &rlist); 87 | } else { 88 | // Data queued for client, so write to it 89 | FD_SET(client, &wlist); 90 | } 91 | 92 | ret = select(maxfd, &rlist, &wlist, &elist, &tv); 93 | if (pipe_error) { break; } 94 | 95 | if (FD_ISSET(target, &elist)) { 96 | handler_emsg("target exception\n"); 97 | break; 98 | } 99 | if (FD_ISSET(client, &elist)) { 100 | handler_emsg("client exception\n"); 101 | break; 102 | } 103 | 104 | if (ret == -1) { 105 | handler_emsg("select(): %s\n", strerror(errno)); 106 | break; 107 | } else if (ret == 0) { 108 | //handler_emsg("select timeout\n"); 109 | continue; 110 | } 111 | 112 | if (FD_ISSET(target, &wlist)) { 113 | len = tout_end-tout_start; 114 | bytes = send(target, ws_ctx->tout_buf + tout_start, len, 0); 115 | if (pipe_error) { break; } 116 | if (bytes < 0) { 117 | handler_emsg("target connection error: %s\n", strerror(errno)); 118 | break; 119 | } 120 | tout_start += bytes; 121 | if (tout_start >= tout_end) { 122 | tout_start = tout_end = 0; 123 | traffic(">"); 124 | } else { 125 | traffic(">."); 126 | } 127 | } 128 | 129 | if (FD_ISSET(client, &wlist)) { 130 | len = cout_end-cout_start; 131 | handler_emsg("Sending to client, len:%u\n",len); 132 | bytes = ws_send(ws_ctx, ws_ctx->cout_buf + cout_start, len); 133 | if (pipe_error) { break; } 134 | if (len < 3) { 135 | handler_emsg("len: %d, bytes: %d: %d\n",(int) len, (int) bytes,(int) *(ws_ctx->cout_buf + cout_start)); 136 | } 137 | cout_start += bytes; 138 | if (cout_start >= cout_end) { 139 | cout_start = cout_end = 0; 140 | traffic("<"); 141 | } else { 142 | traffic("<."); 143 | } 144 | } 145 | 146 | if (FD_ISSET(target, &rlist)) { 147 | bytes = recv(target, ws_ctx->cin_buf, DBUFSIZE , 0); 148 | if (pipe_error) { break; } 149 | if (bytes <= 0) { 150 | handler_emsg("target closed connection\n"); 151 | break; 152 | } 153 | cout_start = 0; 154 | #ifdef DEBUG 155 | printf("before encode: "); 156 | for (i=0; i< bytes; i++) { 157 | printf("%u,", (unsigned char) *(ws_ctx->cin_buf+i)); 158 | } 159 | printf("\n"); 160 | #endif 161 | if (strcasecmp(ws_ctx->headers->protocols, "mqtt") != 0) 162 | { 163 | // MQTT protocol. Encode payload (add frame data) 164 | #ifdef DEBUG 165 | printf("MQTT protocol, set opcode 2 (binary)\n"); 166 | #endif 167 | cout_end = encode_mqtt(ws_ctx->cin_buf, bytes, ws_ctx->cout_buf, BUFSIZE, 2); 168 | } 169 | else 170 | { 171 | handler_emsg("Unsupported protocol: %s\n", ws_ctx->headers->protocols); 172 | break; 173 | } 174 | #ifdef DEBUG 175 | printf("encoded: "); 176 | for (i=0; i< cout_end; i++) { 177 | printf("%u,", (unsigned char) *(ws_ctx->cout_buf+i)); 178 | } 179 | printf("\n"); 180 | #endif 181 | if (cout_end < 0) { 182 | handler_emsg("encoding error\n"); 183 | break; 184 | } 185 | traffic("{"); 186 | } 187 | 188 | if (FD_ISSET(client, &rlist)) { 189 | bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1); 190 | if (pipe_error) { break; } 191 | if (bytes <= 0) { 192 | handler_emsg("client closed connection\n"); 193 | break; 194 | } 195 | tin_end += bytes; 196 | #ifdef DEBUG 197 | printf("before decode: "); 198 | for (i=0; i< bytes; i++) { 199 | printf("%u,", (unsigned char) *(ws_ctx->tin_buf+i)); 200 | } 201 | printf("\n"); 202 | #endif 203 | if (strcasecmp(ws_ctx->headers->protocols, "mqtt") != 0) 204 | { 205 | // MQTT protocol. Decode/unmask data 206 | printf("Assuming MQTT\n"); 207 | len = decode_mqtt(ws_ctx->tin_buf + tin_start, tin_end-tin_start, ws_ctx->tout_buf, BUFSIZE-1, &opcode, &left); 208 | } 209 | else 210 | { 211 | handler_emsg("Unsupported protocol: %s\n", ws_ctx->headers->protocols); 212 | break; 213 | } 214 | 215 | if (opcode == 8) { 216 | handler_emsg("client sent orderly close frame\n"); 217 | break; 218 | } 219 | #ifdef DEBUG 220 | printf("decoded: "); 221 | for (i=0; i< len; i++) { 222 | printf("%u,", (unsigned char) *(ws_ctx->tout_buf+i)); 223 | } 224 | printf("\n"); 225 | #endif 226 | if (len < 0) { 227 | handler_emsg("decoding error\n"); 228 | break; 229 | } 230 | if (left) { 231 | tin_start = tin_end - left; 232 | printf("partial frame from client"); 233 | } else { 234 | tin_start = 0; 235 | tin_end = 0; 236 | } 237 | 238 | traffic("}"); 239 | tout_start = 0; 240 | tout_end = len; 241 | } 242 | } 243 | } 244 | 245 | void proxy_handler(ws_ctx_t *ws_ctx) { 246 | int tsock = 0; 247 | struct sockaddr_in taddr; 248 | 249 | handler_msg("connecting to: %s:%d\n", target_host, target_port); 250 | 251 | tsock = socket(AF_INET, SOCK_STREAM, 0); 252 | if (tsock < 0) { 253 | handler_emsg("Could not create target socket: %s\n", 254 | strerror(errno)); 255 | return; 256 | } 257 | bzero((char *) &taddr, sizeof(taddr)); 258 | taddr.sin_family = AF_INET; 259 | taddr.sin_port = htons(target_port); 260 | 261 | /* Resolve target address */ 262 | if (resolve_host(&taddr.sin_addr, target_host) < -1) { 263 | handler_emsg("Could not resolve target address: %s\n", 264 | strerror(errno)); 265 | } 266 | 267 | if (connect(tsock, (struct sockaddr *) &taddr, sizeof(taddr)) < 0) { 268 | handler_emsg("Could not connect to target: %s\n", strerror(errno)); 269 | close(tsock); 270 | return; 271 | } 272 | 273 | if ((settings.verbose) && (! settings.daemon)) { 274 | printf("%s", traffic_legend); 275 | } 276 | 277 | do_proxy(ws_ctx, tsock); 278 | 279 | shutdown(tsock, SHUT_RDWR); 280 | close(tsock); 281 | } 282 | 283 | int main(int argc, char *argv[]) 284 | { 285 | int fd, c, option_index = 0; 286 | static int ssl_only = 0, daemon = 0, run_once = 0, verbose = 0; 287 | char *found; 288 | static struct option long_options[] = { 289 | {"verbose", no_argument, &verbose, 'v'}, 290 | {"ssl-only", no_argument, &ssl_only, 1 }, 291 | {"daemon", no_argument, &daemon, 'D'}, 292 | /* ---- */ 293 | {"run-once", no_argument, 0, 'r'}, 294 | {"cert", required_argument, 0, 'c'}, 295 | {"key", required_argument, 0, 'k'}, 296 | {0, 0, 0, 0} 297 | }; 298 | 299 | settings.cert = realpath("self.pem", NULL); 300 | if (!settings.cert) { 301 | /* Make sure it's always set to something */ 302 | settings.cert = "self.pem"; 303 | } 304 | settings.key = ""; 305 | 306 | while (1) { 307 | c = getopt_long (argc, argv, "vDrc:k:", 308 | long_options, &option_index); 309 | 310 | /* Detect the end */ 311 | if (c == -1) { break; } 312 | 313 | switch (c) { 314 | case 0: 315 | break; // ignore 316 | case 1: 317 | break; // ignore 318 | case 'v': 319 | verbose = 1; 320 | break; 321 | case 'D': 322 | daemon = 1; 323 | break; 324 | case 'r': 325 | run_once = 1; 326 | break; 327 | case 'c': 328 | settings.cert = realpath(optarg, NULL); 329 | if (! settings.cert) { 330 | usage("No cert file at %s\n", optarg); 331 | } 332 | break; 333 | case 'k': 334 | settings.key = realpath(optarg, NULL); 335 | if (! settings.key) { 336 | usage("No key file at %s\n", optarg); 337 | } 338 | break; 339 | default: 340 | usage(""); 341 | } 342 | } 343 | settings.verbose = verbose; 344 | settings.ssl_only = ssl_only; 345 | settings.daemon = daemon; 346 | settings.run_once = run_once; 347 | 348 | if ((argc-optind) != 2) { 349 | usage("Invalid number of arguments\n"); 350 | } 351 | 352 | found = strstr(argv[optind], ":"); 353 | if (found) { 354 | memcpy(settings.listen_host, argv[optind], found-argv[optind]); 355 | settings.listen_port = strtol(found+1, NULL, 10); 356 | } else { 357 | settings.listen_host[0] = '\0'; 358 | settings.listen_port = strtol(argv[optind], NULL, 10); 359 | } 360 | optind++; 361 | if (settings.listen_port == 0) { 362 | usage("Could not parse listen_port\n"); 363 | } 364 | 365 | found = strstr(argv[optind], ":"); 366 | if (found) { 367 | memcpy(target_host, argv[optind], found-argv[optind]); 368 | target_port = strtol(found+1, NULL, 10); 369 | } else { 370 | usage("Target argument must be host:port\n"); 371 | } 372 | if (target_port == 0) { 373 | usage("Could not parse target port\n"); 374 | } 375 | 376 | if (ssl_only) { 377 | if (access(settings.cert, R_OK) != 0) { 378 | usage("SSL only and cert file '%s' not found\n", settings.cert); 379 | } 380 | } else if (access(settings.cert, R_OK) != 0) { 381 | fprintf(stderr, "Warning: '%s' not found\n", settings.cert); 382 | } 383 | 384 | settings.handler = proxy_handler; 385 | start_server(); 386 | 387 | } 388 | --------------------------------------------------------------------------------