基于 Navicat™ 15 提供的隧道脚本,与 Navicat™ 版权无关的研究。
保留所有权利。未经作者许可不得转载。
请求格式
Navicat™ HTTP 代理基于简单 HTTP。用 通信使用 POST 以 multipart/form-data
为基准提交数据操作信息。
每次提交的表单需要包含如下的数据:
其中,host
不需要包含端口号,q[]
为可空。actn
只有两种值:
C
:表示建立数据库连接Q
:表示执行数据库查询
理论上,Navicat™ 自身没有限制单次请求的数据尺寸,但是在实现中限制为 2,097,152
字节(2MiB
)。
回复格式
Navicat™ 协议的回复是二进制格式的数据。数据类型分为头和块两种,基本顺序为:
- 数据库状态头
- 数据库消息块
- 查询状态头
- 查询消息块
- 查询返回表头
- 查询返回行块
其中,数据库指代数据库服务器实例(Database Instance),查询指代每一个发送到服务器的独立 SQL 查询(Query),返回的结果中,每个查询结果数据使用字节 x01
分割,使用 x00
表示结束。
块
块相对于头来说更加简单,块由正文长度前缀以及正文组成。
如果正文小于 254
个字节,那么头的长度前缀为长度的字符形式。否则写入 xFE
,后跟正文长度的 16
进制 32 位无符号整型。正文直接是原始数据的 UTF-8 字符数组。
注意,块大小限制在4,294,967,295
个字节(4GiB
)长度。
数据库状态头
其中,数据库状态头是所有回复的第一组内容。数据库状态头由下面几段二进制组成(所有的数据都是大端序)
- 32 位无符号整型
1111
- 16 位无符号整型
202
- 32 位无符号整型错误状态码
- 6 个
x00
填充
其中,错误码只有两个数字:
1
表示存在错误0
表示不存在错误。
如果存在错误(错误码为 1
)那么便以数据块的形式写入错误资讯。如果没有错误,那么将写入查询状态头。
查询状态头
查询状态头说明了数据查询的概要信息,包括是否出错,影响行数,最后插入行标识以及后续跟随的行数据的规模(多少条)以及尺寸(多少字段),组成:
- 32 位无符号整型错误码
- 32 位无符号整型受影响行数
- 32 位无符号整型最后插入行 ID
- 32 位无符号整型返回行字段数量
- 32 位无符号整型返回行数量
- 12 个
x00
填充
其中,错误码只有两个数字:
1
表示存在错误0
表示不存在错误。
如果存在错误(错误码为 1
)那么便以数据块的形式写入错误资讯。如果没有错误,那么将写入查询返回表头。
查询返回表头
查询返回表头说明了后面跟随的表的字段信息。数据由多个数据块构成,典型数据块是:
- 表头名块
- 表名块
块后跟
- 32 位无符号整型数据库类型代号(OID)
- 32 位无符号整型数据库字段描述符
- 32 位无符号整型类型尺寸
值得注意的是,字段与字段之间是没有显著分隔字符的。所有的数据都会严格按照顺序写入。
查询返回行块
该块内容直接按照左右上下的顺序写入单元格数据。除 NULL
单元格写入 xFF
外,每个单元格数据均以数据块的形式写入。
示例
获取连接信息
00000000 00 00 04 57 00 ca 00 00 00 00 00 00 00 00 00 00 W
00000010 05 4d 79 53 51 4c 0d 76 69 61 20 54 43 50 2f 49 MySQL via TCP/I
00000020 50 20 31 30 10 53 51 4c 69 6e 6b 20 66 6f 72 20 P 10 SQLink for
00000030 4d 79 53 51 4c MySQL
- 序列
[00000000]
- 32 位无符号整型
1111
→00 00 04 57
- 16 位无符号整型
202
→00 ca
- 32 位无符号整型错误状态码 →
00 00 00 00
- 6 个
x00
填充 →00 00 00 00 00 00
32 位无符号整型为 4 字节,32 比特。16 位无符号整型为 2 字节,16 比特。
- 序列
[00000010 - 00000030]
- 数据库 Host 信息块
MySQL
,长度 5 →05
+4d 79 53 51 4c
- 数据库协议信息块
via TCP/IP 10
,长度 13 →0d
+76 69 61 20 54 43 50 2f 49 50 20 31 30
- 数据库版本块
SQLink for MySQL
,长度 16 →10
+53 51 4c 69 6e 6b 20 66 6f 72 20 4d 79 53 51 4c
查询 SELECT * FROM AA.test123 LIMIT 0,1000
00000000 00 00 04 57 00 ca 00 00 00 00 00 00 00 00 00 00 W
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03
00000020 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00
00000030 02 69 64 00 00 00 00 03 00 00 50 03 00 00 00 0b id P
00000040 04 6e 61 6d 65 00 00 00 00 fc 00 00 00 10 00 02 name
00000050 ff fd 07 63 6f 6d 6d 65 6e 74 00 00 00 00 fc 00 comment
00000060 00 00 10 00 02 ff fd 01 31 03 79 65 70 ff 01 32 1 yep 2
00000070 03 79 65 70 ff 00 yep
数据返回预览:
查询状态头
- 序列
[00000000]
与上方示例相同,不再赘述。 - 序列
[00000010] - [00000020]
- 32 位无符号整型错误码:无错误,0 →
00 00 00 00
- 32 位无符号整型受影响行数:SELECT 语句没有更新,0 →
00 00 00 00
- 32 位无符号整型最后插入行 ID:SELECT 语句没有更新,0 →
00 00 00 00
- 32 位无符号整型返回行字段数量,3 →
00 00 00 03
- 32 位无符号整型返回行数量 2 →
00 00 00 02
- 12 个
x00
填充 →00 00 00 00 00 00 00 00 00 00 00 00
查询返回表头
[00000030]
id
,长度 2,类型int
→02
+69 64
+00
- OID
3
+ 描述符20483
+ 长度 11 →00 00 00 03
+00 00 50 03
+00 00 00 0b
[00000040] - [00000050]
name
,长度 4,类型text
→04
+6e 61 6d 65
+00
- OID
252
+ 描述符16
+ 长度 196605 →00 00 00 fc
+00 00 00 10
+00 02 ff fd
[00000050] - [00000060]
comment
,长度 7,类型text
→07
+63 6f 6d 6d 65 6e 74
+00
- OID
252
+ 描述符16
+ 长度 196605 →00 00 00 fc
+00 00 00 10
+00 02 ff fd
OID 以及描述符来自数据库官方,一般数据库驱动会提供。
查询返回行块
[00000060] - [00000070]
分隔符
没有更多结果集(ResultSet),所以以 00
结束。
练习
没错,上个 知乎 读篇文章居然还要做练习!
Navicat™ 兼容的一段二进制:
00000000 00 00 04 57 00 ca 00 00 00 00 00 00 00 00 00 00 W
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000030 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000040 00 08 00 00 00 02 00 00 00 00 00 00 00 00 00 00
00000050 00 00 03 6d 69 64 00 00 00 00 17 00 00 00 00 00 mid
00000060 00 00 00 04 6e 61 6d 65 00 00 00 04 13 00 00 00 name
00000070 00 00 00 00 c8 04 73 6c 75 67 00 00 00 04 13 00 slug
00000080 00 00 00 00 00 00 c8 04 74 79 70 65 00 00 00 04 type
00000090 13 00 00 00 00 00 00 00 10 0b 64 65 73 63 72 69 descri
000000a0 70 74 69 6f 6e 00 00 00 04 13 00 00 00 00 00 00 ption
000000b0 00 c8 05 63 6f 75 6e 74 00 00 00 00 17 00 00 00 count
000000c0 00 00 00 00 00 05 6f 72 64 65 72 00 00 00 00 17 order
000000d0 00 00 00 00 00 00 00 00 06 70 61 72 65 6e 74 00 parent
000000e0 00 00 00 17 00 00 00 00 00 00 00 00 01 31 0c e9 1
000000f0 bb 98 e8 ae a4 e5 88 86 e7 b1 bb 07 64 65 66 61 认分类 defa
00000100 75 6c 74 08 63 61 74 65 67 6f 72 79 18 e5 8f aa ult category 只
00000110 e6 98 af e4 b8 80 e4 b8 aa e9 bb 98 e8 ae a4 e5 是一个默认
00000120 88 86 e7 b1 bb 01 30 01 31 01 30 01 32 06 e6 8a 类 0 1 0 2
00000130 80 e6 9c af 09 74 65 63 68 6e 69 63 61 6c 08 63 术 technical c
00000140 61 74 65 67 6f 72 79 ff 01 32 01 32 01 30 00 ategory 2 2 0
请尝试分析表格数据并标注类型。