-
-
[原创]物联网防火墙himqtt源码之MQTT协议分析
-
发表于: 2019-10-31 09:50 9798
-
himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWall,C语言编写,采用epoll模式支持IoT数十万的高并发连接,并且兼容ModSecurity部分规则。 代码非常优秀,非常值得收藏和学习,今天笔者就从结合himqtt的源码来进行MQTT协议分析。
MQTT协议一共有14个指令,如下表所示:其中有9个报文都是固定的2~4个字节,非常简单适合小型物联网设备。
MQTT协议由指令号(1字节)+长度(1-4字节不定)+内容组成,比如下面第一个字节0x30表示publish发布消息指令,0x26表示后面的内容长度就是38个字节。
---------------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最新源码,49bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6I4M7e0b7I4x3o6R3^5y4U0y4Q4x3V1k6Z5K9h3#2I4N6s2c8Q4x3V1k6Q4x3U0k6F1j5Y4y4H3i4K6y4n7i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1$3i4K6R3&6i4K6V1K6i4@1f1#2i4@1u0o6i4K6R3H3M7%4u0U0i4K6u0r3N6$3q4X3i4K6u0r3L8i4q4@1N6q4)9J5k6h3y4Q4c8e0k6Q4z5e0k6Q4z5o6N6Q4c8e0c8Q4b7V1u0Q4b7U0k6Q4c8e0y4Q4z5o6m8Q4z5o6t1`.
特别注意的是:长度占用的字节数是可变的(1-4字节),具体的计算方法在process_mqtt_msg这个函数里面,理论上这种算法后续消息内容是最大长度是268435455字节(约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;
}
}
.....
}
......
长度和协议校验正确后,根据收到的消息类型,以此对不同的指令进行处理,代码逻辑非常清晰:
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这几个复杂一点的报文协议内容。
CONNECT是客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT报文,其中登录的身份认证如用户名、密码就在这个指令里面。报文协议如下:
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课