【Mysql源码分析】mysql协议分析(一)
来源:SegmentFault
时间:2023-01-28 11:27:24 325浏览 收藏
本篇文章给大家分享《【Mysql源码分析】mysql协议分析(一)》,覆盖了数据库的常见基础知识,其实一个语言的全部知识点一篇文章是不可能说完的,但希望通过这些问题,让读者对自己的掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握它。
# 前言
在近期的mysql学习过程中,一直想做的一件事就是分析下mysql的协议。了解Mysql协议也是深入了解Mysql的过程。本章的内容由于篇幅过大,会拆分成两章来讲述。
Mysql版本:8.0.20
抓包工具:Wireshark
1.Mysql协议分析
在分析mysql之前我们首先要把mysql的ssl关掉,抓包过程中TLS解析比较麻烦。
1.1 关闭Mysql SSL
可以通过配置my.cnf文件加入skip_ssl指令关闭ssl,配置如下:
[mysqld] skip_ssl
关闭SSL之后重启mysql服务,可以通过“show global variables like 'have_ssl%';” 查看ssl关闭情况,如图1-1-1。
通过客户端直接连接mysql的话可能会报“ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.”错误,此时可以通过加入“--connect-expired-password” 参数进行访问。默认情况下Mysql8.0.20会采用caching_sha2_password插件进行密码交互,会存在缓存。此参数是密码的沙箱模式缓存过期。Mysql8.0以下版本采用mysql_native_password插件进行交互。
其次如果要抓包的话,连接客户端需要使用参数“--protocol=tcp”进行访问,这样方便抓包。
1.2 Mysql 抓包
抓包之前我们明确一下我们要抓包的内容,抓包内容如下:
$bin/mysql -h localhost -u root -p'123456' --protocol=tcp mysql>use t1; mysql>select * from t1;
1.2.1 Mysql 抓包
语句中我们只有两条语句,执行过程如图1-2-1:
我们直接使用Wireshark进行抓包,抓包格式如下:
No. | Source | Sport | Dport | Protocol | Length | Info |
---|---|---|---|---|---|---|
1 | localhost | 56933 | 3306 | TCP | 68 | 56933 → 3306 [SYN] Seq=0 Win=65535 Len=0 MSS=16344 WS=64 TSval=282542188 TSecr=0 SACK_PERM=1 |
2 | localhost | 3306 | 56933 | TCP | 68 | 3306 → 56933 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=16344 WS=64 TSval=282542188 TSecr=282542188 SACK_PERM=1 |
3 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=282542188 TSecr=282542188 |
4 | localhost | 3306 | 56933 | TCP | 56 | [TCP Window Update] 3306 → 56933 [ACK] Seq=1 Ack=1 Win=408256 Len=0 TSval=282542188 TSecr=282542188 |
5 | localhost | 3306 | 56933 | MySQL | 134 | Server Greeting proto=10 version=8.0.20 |
6 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=1 Ack=79 Win=408192 Len=0 TSval=282542188 TSecr=282542188 |
7 | localhost | 56933 | 3306 | MySQL | 268 | Login Request user=root |
8 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=79 Ack=213 Win=408064 Len=0 TSval=282542188 TSecr=282542188 |
9 | localhost | 3306 | 56933 | MySQL | 62 | Auth Switch Request |
10 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=213 Ack=85 Win=408192 Len=0 TSval=282542188 TSecr=282542188 |
11 | localhost | 3306 | 56933 | MySQL | 67 | Response OK |
12 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=213 Ack=96 Win=408192 Len=0 TSval=282542188 TSecr=282542188 |
13 | localhost | 56933 | 3306 | MySQL | 93 | Request Query |
14 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=96 Ack=250 Win=408000 Len=0 TSval=282542188 TSecr=282542188 |
15 | localhost | 3306 | 56933 | MySQL | 139 | Response |
16 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=250 Ack=179 Win=408064 Len=0 TSval=282542188 TSecr=282542188 |
17 | localhost | 56933 | 3306 | MySQL | 78 | Request Query |
18 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=179 Ack=272 Win=408000 Len=0 TSval=282558403 TSecr=282558403 |
19 | localhost | 3306 | 56933 | MySQL | 113 | Response |
20 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=272 Ack=236 Win=408064 Len=0 TSval=282558403 TSecr=282558403 |
21 | localhost | 56933 | 3306 | MySQL | 63 | Request Use Database |
22 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=236 Ack=279 Win=408000 Len=0 TSval=282558403 TSecr=282558403 |
23 | localhost | 3306 | 56933 | MySQL | 74 | Response OK |
24 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=279 Ack=254 Win=408000 Len=0 TSval=282558403 TSecr=282558403 |
25 | localhost | 56933 | 3306 | MySQL | 75 | Request Query |
26 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=254 Ack=298 Win=408000 Len=0 TSval=282558404 TSecr=282558404 |
27 | localhost | 3306 | 56933 | MySQL | 214 | Response |
28 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=298 Ack=412 Win=407872 Len=0 TSval=282558407 TSecr=282558407 |
29 | localhost | 56933 | 3306 | MySQL | 72 | Request Query |
30 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=412 Ack=314 Win=407936 Len=0 TSval=282558407 TSecr=282558407 |
31 | localhost | 3306 | 56933 | MySQL | 152 | Response |
32 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=314 Ack=508 Win=407744 Len=0 TSval=282558409 TSecr=282558409 |
33 | localhost | 56933 | 3306 | MySQL | 67 | Request Show Fields |
34 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=508 Ack=325 Win=407936 Len=0 TSval=282558409 TSecr=282558409 |
35 | localhost | 3306 | 56933 | MySQL | 275 | Response |
36 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=325 Ack=727 Win=407552 Len=0 TSval=282558415 TSecr=282558415 |
37 | localhost | 56933 | 3306 | MySQL | 64 | Request Show Fields |
38 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=727 Ack=333 Win=407936 Len=0 TSval=282558415 TSecr=282558415 |
39 | localhost | 3306 | 56933 | MySQL | 140 | Response |
40 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=333 Ack=811 Win=407488 Len=0 TSval=282558415 TSecr=282558415 |
41 | localhost | 56933 | 3306 | MySQL | 64 | Request Show Fields |
42 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=811 Ack=341 Win=407936 Len=0 TSval=282558415 TSecr=282558415 |
43 | localhost | 3306 | 56933 | MySQL | 140 | Response |
44 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=341 Ack=895 Win=407360 Len=0 TSval=282558416 TSecr=282558416 |
45 | localhost | 56933 | 3306 | MySQL | 77 | Request Query |
46 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=895 Ack=362 Win=407936 Len=0 TSval=282563656 TSecr=282563656 |
47 | localhost | 3306 | 56933 | MySQL | 166 | Response |
48 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=362 Ack=1005 Win=407296 Len=0 TSval=282563658 TSecr=282563658 |
49 | localhost | 56933 | 3306 | MySQL | 61 | Request Quit |
50 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=1005 Ack=367 Win=407872 Len=0 TSval=282585990 TSecr=282585990 |
51 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [FIN, ACK] Seq=367 Ack=1005 Win=407296 Len=0 TSval=282585990 TSecr=282585990 |
52 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [ACK] Seq=1005 Ack=368 Win=407872 Len=0 TSval=282585990 TSecr=282585990 |
53 | localhost | 3306 | 56933 | TCP | 56 | 3306 → 56933 [FIN, ACK] Seq=1005 Ack=368 Win=407872 Len=0 TSval=282585990 TSecr=282585990 |
54 | localhost | 56933 | 3306 | TCP | 56 | 56933 → 3306 [ACK] Seq=368 Ack=1006 Win=407232 Len=0 TSval=282585990 TSecr=282585990 |
如表1-2-2所示,“No.”编号 1~3 为TCP的三次握手。“No.”编号 51~54为TCP的四次挥手部分。其余部分为Mysql的通信数据部分。
根据表1-2-2中抓包数据,我们可以得到如图1-2-3,我们的一个数据包交互流程。
在分析数据包时,会有Request Command的Server Command编码该编码在/mysql-8.0.20/include/my_command.h文件中:
enum enum_server_command { COM_SLEEP, /** 0 */ COM_QUIT, /** 1 退出,可以看page_protocol_com_quit */ COM_INIT_DB, /** 2 初始化数据库, 可以看page_protocol_com_init_db */ COM_QUERY, /** 3 查询,可以看 page_protocol_com_query */ COM_FIELD_LIST, /** 4 列举字段,可以看 page_protocol_com_field_list */ COM_CREATE_DB, /** 5 创建数据库,可以看 ::dispatch_command */ COM_DROP_DB, /** 6 删除数据库,可以看 ::dispatch_command */ COM_REFRESH, /** 7 刷新,可以看 page_protocol_com_refresh */ COM_DEPRECATED_1, /** 8 < Deprecated, used to be COM_SHUTDOWN */ COM_STATISTICS, /** 9 查询服务端内部统计,可以看 page_protocol_com_statistics */ COM_PROCESS_INFO, /** 10 查看processs list 可以看 page_protocol_com_process_info */ COM_CONNECT, /** 11 当服务器被拒绝,连接 */ COM_PROCESS_KILL, /** 12 process kill,可以看 page_protocol_com_process_kill */ COM_DEBUG, /** 13 debug,可以看 page_protocol_com_debug */ COM_PING, /** 14 ping,可以看page_protocol_com_ping */ COM_TIME, /** 15 当服务器被拒绝 */ COM_DELAYED_INSERT, /** 16 功能已删除 */ COM_CHANGE_USER, /** 17 修改用户,可以看 page_protocol_com_change_user */ COM_BINLOG_DUMP, /** 18 binlog dump,可以看 page_protocol_com_binlog_dump */ COM_TABLE_DUMP, /** 19 dumptable */ COM_CONNECT_OUT, /** 20 服务器中的内部命令 */ COM_REGISTER_SLAVE, /** 21 在主服务器上注册一个从服务器。应该在使用COM_BINLOG_DUMP请求binlog事件之前发送 。 */ COM_STMT_PREPARE, /** 22 根据传递的查询字符串创建准备好的语句,例如“SELECT CONC 41 54 28 3f 2c 20 3f 29 20 41 53 20 63 6f 6c 31 AT(?, ?) AS col1”,可以看 page_protocol_com_stmt_prepare */ COM_STMT_EXECUTE, /** 23 要求服务器执行由标识的准备好的语句stmt-id,可以看 page_protocol_com_stmt_execute */ COM_STMT_SEND_LONG_DATA, /** 24 发送列的数据。重复发送,将数据追加到参数中。可以看 page_protocol_com_stmt_send_long_data */ COM_STMT_CLOSE, /** 25 取消分配准备好的语句、可以看 page_protocol_com_stmt_close */ COM_STMT_RESET, /** 26 重置通过COM_STMT_SEND_LONG_DATA 命令累积的准备好的语句的数据, 如果使用COM_STMT_EXECUTE打开了该游标,则关闭该游标、可以看 page_protocol_com_stmt_reset */ COM_SET_OPTION, /** 27 设置参数,比如启用和禁用当前连接的功CLIENT_MULTI_STATEMENTS、可以设置MYSQL_OPTION_MULTI_STATEMENTS_ON和MYSQL_OPTION_MULTI_STATEMENTS_OFF,可以看 page_protocol_com_set_option */ COM_STMT_FETCH, /** 28 在COM_STMT_EXECUTE之后从现有结果集中获取行 。可以看。 page_protocol_com_stmt_fetch */ /** Currently refused by the server. See ::dispatch_command. Also used internally to mark the session as a "daemon", i.e. non-client THD. Currently the scheduler and the GTID code does use this state. These threads won't be killed by `KILL` @sa Event_scheduler::start, ::init_thd, ::kill_one_thread, ::Find_thd_with_id */ COM_DAEMON, /** 29 服务器中的内部命令 */ COM_BINLOG_DUMP_GTID, /** 30 如果binlog-filename为空,则服务器将发送第一个已知二进制日志的二进制日志流。 */ COM_RESET_CONNECTION, /** 31 重置会话状态;比COM_CHANGE_USER它不会关闭并重新打开连接,并且不会重新进行身份验证 更轻巧。可以看 page_protocol_com_reset_connection */ COM_CLONE, /** 32 克隆 */ /* 别忘了在sql中更新 sql_parse.cc 的const char *command_name[] 中 */ /* 最后一个 */ COM_END /** 33 不是真正的命令. */ };
1.2.2 Mysql 授权版本信息数据包分析
我们首先从数据包第5条开始看,如图1-2-4。
图1-2-4内容可以清晰看到我们Mysql Protocol协议体结构,结构如表1-2-5。
Type | Name | Description |
---|---|---|
int | payload_length | 有效数据包的长度。数据包中超过组成数据包头的4个字节的字节数。 |
int | sequence_id | Sequence ID |
string | payload | 有效的数据包 |
数据结构可以参照官方文档:
https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_packets.html 。我们可以看到图1-2-3中Mysql Protocol结构,payload_length长度为74,sequence_id为1。Server Greeting中可以获取到mysql到版本号,协议,线程ID,Salt等等,该数据包是服务端回复客户端数据。
1.2.3 Mysql 授权数据包分析
接下来我们继续看一下,下一个Mysql数据协议包,登陆数据包。
如图1-2-6客户端往服务端发送了一个Login Request,Login Request 对应Username为root,对应的Client Auth Plugin插件名称为caching_sha2_password。
身份验证开关数据包。
授权ok数据包。
在使用use t1之前,先调用了“select @@version_comment”,查询对应版本备注。Command指令为3,对应1.2.1小节中的COM_QUERY参数。
1.2.4 Mysql 选择数据库数据包分析
/** 插件驱动程序的客户端身份验证。 @注意,mysql_real_connect和mysql_change_用户都使用此选项 @param mysql mysql @param data 插件中的身份验证数据,握手包 @param data_len 数据长度 @param data_plugin 为数据准备的插件如果是mysql_change_ user()则为0 @param db 要使用的初始数据库,可以是0 @retval 0 ok @retval 1 error */ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, const char *data_plugin, const char *db) { DBUG_TRACE; mysql_state_machine_status status; mysql_async_auth ctx; memset(&ctx, 0, sizeof(ctx)); ctx.mysql = mysql; //mysql结构、内部包含连接信息,如:host、user、passwd、unix_socket、server_version等等 ctx.data = data; //验证数据 ctx.data_len = data_len; //数据长度 ctx.data_plugin = data_plugin; //插件数据mysql8.0.20默认为caching_sha2_password ctx.db = db; //初始化数据库 ctx.non_blocking = false; ctx.state_function = authsm_begin_plugin_auth; do { status = ctx.state_function(&ctx); //调用插件处理函数 } while (status != STATE_MACHINE_FAILED && status != STATE_MACHINE_DONE); return status == STATE_MACHINE_FAILED; }
根据如上代码我们可以看到run_plugin_auth函数是调用授权插件,授权插件Mysql8.0.20是默认使用caching_sha2_password,图2-1-2。
caching_sha2_password在authsm_begin_plugin_auth函数中设置,ctx->auth_plugin对应的内容中的caching_sha2_password_plugin_name宏,/mysql-8.0.20/sql-common/client.cc 文件中。
#define caching_sha2_password_plugin_name "caching_sha2_password"
在运行run_plugin_auth函数后,调用插件处理函数最终会调用caching_sha2_password_auth_client进行授权处理。caching_sha2_password_auth_client函数也是在authsm_begin_plugin_auth函数中设置,对应ctx->auth_plugin。
总结
(1)Mysql 8.0.20 采用caching_sha2_password插件进行密码验证。Mysql8.0之前是使用mysql_native_password插件进行密码验证。
(2)caching_sha2_password密码验证方式有缓存。
(3)“use 数据库; ”语句会发多条查询包。
- 1.select DATABASE()
- 2.use t1
- 3.show databases
- 4.show tables
今天关于《【Mysql源码分析】mysql协议分析(一)》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
214 收藏
-
155 收藏
-
485 收藏
-
436 收藏
-
125 收藏
-
174 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 壮观的大门
- 这篇技术贴出现的刚刚好,太细致了,受益颇多,已收藏,关注up主了!希望up主能多写数据库相关的文章。
- 2023-03-11 06:47:53
-
- 纯情的芒果
- 很详细,码起来,感谢作者大大的这篇技术文章,我会继续支持!
- 2023-03-05 07:05:01
-
- 忧伤的柚子
- 赞 👍👍,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢大佬分享文章内容!
- 2023-02-20 03:49:59
-
- 瘦瘦的香菇
- 这篇博文出现的刚刚好,作者大大加油!
- 2023-02-10 10:49:13
-
- 瘦瘦的火龙果
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢up主分享技术贴!
- 2023-01-31 20:02:11
-
- 追寻的美女
- 这篇文章内容真是及时雨啊,细节满满,很好,收藏了,关注up主了!希望up主能多写数据库相关的文章。
- 2023-01-29 01:16:59