應(yīng)用

技術(shù)

物聯(lián)網(wǎng)世界 >> 物聯(lián)網(wǎng)新聞 >> 物聯(lián)網(wǎng)熱點新聞
企業(yè)注冊個人注冊登錄

物聯(lián)網(wǎng)防火墻himqtt源碼之MQTT協(xié)議分析

2019-11-04 09:14 媒體投稿

導(dǎo)讀:himqtt是首款完整源碼的高性能MQTT物聯(lián)網(wǎng)防火墻 - MQTT Application FireWall,C語言編寫,采用epoll模式支持IoT數(shù)十萬的高并發(fā)連接,并且兼容ModSecurity部分規(guī)則。 代碼非常優(yōu)秀,非常值得收藏和學(xué)習(xí),今天筆者就從結(jié)合himqtt的源碼來進(jìn)行MQTT協(xié)議分析。

物聯(lián)網(wǎng)防火墻himqtt源碼之MQTT協(xié)議分析

himqtt是首款完整源碼的高性能MQTT物聯(lián)網(wǎng)防火墻 - MQTT Application FireWall,C語言編寫,采用epoll模式支持IoT數(shù)十萬的高并發(fā)連接,并且兼容ModSecurity部分規(guī)則。 代碼非常優(yōu)秀,非常值得收藏和學(xué)習(xí),今天筆者就從結(jié)合himqtt的源碼來進(jìn)行MQTT協(xié)議分析。

一、MQTT協(xié)議指令匯總

MQTT協(xié)議一共有14個指令,如下表所示:其中有9個報文都是固定的2~4個字節(jié),非常簡單適合小型物聯(lián)網(wǎng)設(shè)備。

名字值固定報文描述

CONNECT1否客戶端請求與服務(wù)端建立連接

CONNACK2是服務(wù)端確認(rèn)連接建立

PUBLISH3否發(fā)布消息

PUBACK4是收到發(fā)布消息確認(rèn)

PUBREC5是發(fā)布消息收到

PUBREL6是發(fā)布消息釋放

PUBCOMP7是發(fā)布消息完成

SUBSCRIBE8否訂閱請求

SUBACK9否訂閱確認(rèn)

UNSUBSCRIBE10否取消訂閱

UNSUBACK11是取消訂閱確認(rèn)

PING12是客戶端發(fā)送PING(連接?;?命令

PINGRSP13是PING命令回復(fù)

DISCONNECT14是斷開連接

MQTT協(xié)議由指令號(1字節(jié))+長度(1-4字節(jié)不定)+內(nèi)容組成,比如下面第一個字節(jié)0x30表示publish發(fā)布消息指令,0x26表示后面的內(nèi)容長度就是38個字節(jié)。

---------------MQTT PUBLISH- ------40bytes-------------------------------------------

| 30 26 00 14 68 6f 6d 65 2f 67 61 72 64 65 6e 2f |0&..home/garden/|

| 66 6f 75 6e 74 61 69 6e 31 32 33 34 35 36 37 38 |fountain12345678|

| 39 30 61 62 63 64 65 66 |90abcdef

先到github上下載himqtt最新源碼,https://github.com/qq4108863/himqtt/ ,打開src/waf/mqtt.c文件。

特別注意的是:長度占用的字節(jié)數(shù)是可變的(1-4字節(jié)),具體的計算方法在process_mqtt_msg這個函數(shù)里面,理論上這種算法后續(xù)消息內(nèi)容是最大長度是268435455字節(jié)(約255M)。

static void process_mqtt_msg(mqtt_waf_msg *req)

{

......

/* decode mqtt variable length */

len = len_len = 0;

p = req->buf + 1;

eop = &req->buf[req->pos];

while (p < eop) {

lc = *((const unsigned char *) p++);

len += (lc & 0x7f) << 7 * len_len;

len_len++;

if (!(lc & 0x80)) break;

if (len_len > 4){

req->msg_state = MQTT_MSG_ERROR;

return;

}

}

.....

}

......

長度和協(xié)議校驗正確后,根據(jù)收到的消息類型,以此對不同的指令進(jìn)行處理,代碼邏輯非常清晰:

switch (mqtt_msg_type)

{

case MQTT_CONNECT:

req->msg_state = mqtt_connect(req,p,end,&mm);

break;

case MQTT_CONNACK:

break;

case MQTT_PUBLISH:

req->msg_state = mqtt_publish(req,p,end,&mm);

break;

case MQTT_SUBSCRIBE:

req->msg_state = mqtt_subscribe(req,p,end,&mm);

break;

case MQTT_UNSUBSCRIBE:

req->msg_state = mqtt_unsubscribe(req,p,end,&mm);

......

下面我們主要CONNECT、PUBLISH、SUBSCRIBE、UNSUBSCRIBE這幾個復(fù)雜一點的報文協(xié)議內(nèi)容。

二、HiMQTT協(xié)議分析

1、CONNECT連接服務(wù)端

CONNECT是客戶端到服務(wù)端的網(wǎng)絡(luò)連接建立后,客戶端發(fā)送給服務(wù)端的第一個報文必須是CONNECT報文,其中登錄的身份認(rèn)證如用戶名、密碼就在這個指令里面。報文協(xié)議如下:

--------------MQTT CONNECT-----105bytes-----------------------------

| 10 67 00 04 4d 51 54 54 04 c2 00 3c 00 19 4d 51 |.g..MQTT...<..MQ|

| 54 54 5f 46 58 5f 43 6c 69 65 6e 74 5f 39 69 75 |TT_FX_Client_9iu|

| 79 38 37 36 35 35 35 00 12 69 6f 74 66 72 65 65 |y876555..iotfree|

| 74 65 73 74 2f 74 68 69 6e 67 30 00 2c 59 55 37 |test/thing0.,YU7|

| 54 6f 76 38 7a 46 57 2b 57 75 61 4c 78 39 73 39 |Tov8zFW+WuaLx9s9|

| 49 33 4d 4b 79 63 6c 69 65 39 53 47 44 75 75 4e |I3MKyclie9SGDuuN|

| 6b 6c 36 6f 39 4c 58 6f 3d |kl6o9LXo=

10 //CONNECT指令號

67 //長度103字節(jié)

00 04 //MQTT協(xié)議長度為4字節(jié)

4d 51 54 54 //MQTT固定字符串

04 //版本3.1.1

c2 //連接標(biāo)記,是否由用戶名/密碼等

00 3c //心跳間隔時間60秒

00 19 //用戶名長度25字節(jié),后面是用戶名

4d 51 54 54 5f 46 58 5f 43 6c 69 65 6e 74 5f 39 69 75 79 38 37 36 35 35 35

00 12 //密碼18字節(jié)

69 6f 74 66 72 65 65 74 65 73 74 2f 74 68 69 6e 67 30//密碼

00 2c //will message長度。

59 55 37 ......6f 3d//will message內(nèi)容

2、PUBLISH發(fā)布消息

PUBLISH 是從客戶端向服務(wù)端或者服務(wù)端向客戶端傳輸一個應(yīng)用消息,這是通信的最重點,就像HTTP協(xié)議的GET一樣。報文協(xié)議分析如下:

/*

---------------MQTT PUBLISH-------40bytes-----------------------------

| 30 26 00 14 68 6f 6d 65 2f 67 61 72 64 65 6e 2f |0&..home/garden/|

| 66 6f 75 6e 74 61 69 6e 31 32 33 34 35 36 37 38 |fountain12345678|

| 39 30 61 62 63 64 65 66 |90abcdef

*/

30 //PUBLISH指令號

26 //長度39字節(jié)

00 14 //TOPIC長度20字節(jié)

68 6f 6d 65 2f 67 61 72 64 65 6e 2f 66 6f 75 6e //TOPIC

31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 //發(fā)布的消息

在實際編程中,這個地方大多是json格式提交給服務(wù)器,SQL注入/XSS攻擊很可能從這里對物聯(lián)網(wǎng)設(shè)備發(fā)起攻擊,所以一定要做攻擊檢查,himqtt不知道什么原因把ngx_http_dummy_json_parse解析json格式的函數(shù)注釋掉了。

3、SUBSCRIBE訂閱消息

SUBSCRIBE是由客戶端向服務(wù)端發(fā)送的,用于創(chuàng)建一個或多個訂閱。每個訂閱是該客戶端關(guān)注的一個或多個主題。服務(wù)端依據(jù)客戶端的訂閱來匹配主題,然后將對應(yīng)的PUBLISH報文發(fā)送給客戶端。報文協(xié)議分析如下:

---------------MQTT SUBSCRIBE------33bytes-----------------------------

| 82 1f 00 01 00 1a 68 6f 6d 65 2f 67 61 72 64 65 |......home/garde|

| 6e 2f 66 6f 75 6e 74 61 69 6e 64 65 6c 65 74 65 |n/fountaindelete|

| 00

82 //SUBSCRIBE指令

1f //長度31字節(jié)

00 01 //Message Identifier

00 1a //TOPIC長度26

68 6f 6d 65 2f 67 61 72 64 65 6e 2f 66 6f 75 6e 74 61 69 6e 64 65 6c 65 74 65 //TOPIC

00 //request QOS

4、UNSUBSCRIBE取消訂閱消息

UNSUBSCRIBE是客戶端發(fā)送本報文給服務(wù)端,用于取消訂閱主題。報文協(xié)議分析如下:

---------------MQTT UNSUBSCRIBE-------32bytes-----------------------------

| a2 1e 00 02 00 1a 68 6f 6d 65 2f 67 61 72 64 65 |......home/garde|

| 6e 2f 66 6f 75 6e 74 61 69 6e 64 65 6c 65 74 65 |n/fountaindelete|

a2 //SUBSCRIBE指令

1e //長度30字節(jié)

00 02 //Message Identifier

00 1a //TOPIC長度26

68 6f 6d 65 2f 67 61 72 64 65 6e 2f 66 6f 75 6e 74 61 69 6e 64 65 6c 65 74 65 //TOPIC

總體來說,MQTT協(xié)議比HTTP協(xié)議簡單多了,非常適合物聯(lián)網(wǎng)設(shè)備。另外himqtt其實也是一款功能強(qiáng)大的WEB應(yīng)用防火墻,其他源碼我們在另外的文章中再介紹。

也許未來幾年IPV6普及后,很可能幾百億帶電的物體都會聯(lián)網(wǎng)哦,期待himqtt這類高并發(fā)的物聯(lián)網(wǎng)防火墻能扛起信息安全的大旗,徹底阻擋黑客攻擊。