SSLv2Hello 协议引发的连接拒绝

有开发人员问我, 有没有见过这个 Java 应用报出的异常消息: IO Error: Remote host terminated the handshake. 从这个message看, 这个是说无法通过握手建立连接, 单纯从这个消息看, 看不出来什么原因.

背景

这个应用最近更改了不少文件, 然后重新发布新版本, 就出现了这个错误. 出这个错误的上下文是: 这是一个连接 Oracle 数据的连接, 使用的是TSLv1.2加密连接.

他们问了DBA, DBA 说数据库完好, 问了网络, 网络也没有其它问题.

诊断

最简单的方法是抓包, 查看具体的握手失败的原因. 抓包后看到如下的 SSL client hello 协议内容, 然后服务器就拒绝了:
client_hello.png

从上面的截图我们可以看到, tcp 三次握手, 正常. 然后客户端发送 client hello, 服务器直接就发了 FIN, 要拒绝连接.

在看 client hello 的具体消息, 从消息可以看到, 这里上面写的是 SSLv2, 难道这是1995年使用的 SSL 2.0? 看上去很奇怪.

如果比较熟悉 TLS 协议, 我们可以知道, 在TLS 层, 又分为 TLS record 协议, 它内部又封装了具体的 TLS 子协议, 分别是: Application, Alert, Handshake, ChangeCipherSpec. 而 record 协议的第一个字节表示子协议的类型.

然而我们这个截图里面, 前两个字节是 80 b3, 表示内容长度, 关键是 Wireshark 认识这种格式. 太诡异了.

然后仔细查看 Wireshark 的解析, 我们看到 Wireshark 标注的是: SSLv2 Record Layer: Client Hello.

关于 SSLv2Hello

顺着这个关键字, 我们终于找到了另外一种协议: SSLv2Hello. 这是一种伪协议, 它是 Oracle 之前发明的, 并且现在已经不在使用的. 它的主要作用是在客户端发送一个SSLv2Hello消息, 试探一下服务端使用的到底是哪种协议, 是 TLS 1.1, 还是 TLS 1.2.

JDK 里面在 JDK 7 之前默认是有这种协议的, 之后 Oracle disable 了它.

-- 未完待续 --

Mac 的 localhost & 127.0.0.1 的5000端口拒绝访问

最近开发 Python 应用, 使用 Flask框架, 启动 app 默认开启 5000 端口. 如下:

 * Debugger is active!
 * Debugger PIN: 529-552-853
(9079) wsgi starting up on http://127.0.0.1:5000

一开始的时候, 发现 localhost:5000 无法访问:
localhost.png

只能 127.0.0.1:5000 访问, 虽然无法解释, 但是启动日志写的 127.0.0.1:5000 也能暂时这么使用.

可是, 很快就发现了新问题, 一旦任何一个文件更改, 就会触发app重新load, 然后重启, 这个时候, 就发现连 127.0.0.1:5000 也无法访问了:
127_0_0_1.png

于是我想到了之前遇到的问题: 由于某些cookie过大, 导致某些Python框架处理不过来, 直接不能正常返回. 于是 - 我清理缓存 -> Chrome 浏览器 -> More Tools -> Clear Browsing Data -> Cookies and Site Data.

问题得以暂时解决. 可是, 这带来了副作用-> 每次清理完, 很多登录过的账号, 都要重新登录. 比如 chat.openai.com 和 公司里面的很多工具. 烦.

于是, 想去弄明白, 到底哪里出了问题. 我重新访问 127.0.0.1:5000, 然后去仔细观察请求/回应数据, 发现了端倪:
server.png

AirTunes/675.4.1 这是啥?

诊断

然后看谁打开了5000端口:

eric@host % sudo lsof -i -n -P | grep 5000
Password:
ControlCe   539         eric    7u  IPv4 0xbe799013704c3c99      0t0    TCP *:5000 (LISTEN)
ControlCe   539         eric    8u  IPv6 0xbe799009db509931      0t0    TCP *:5000 (LISTEN)

虽然不能看到完全的进程名, 但是可以看到进程号: 539. 然后打开 Activity Monitor, 然后查看这个进程, 然后看到进程名字是: Control Center, 还有那个熟悉的图标.

于是, 我尝试杀死这个进程, 它又自动重启了, 同时我启动了 Flask app, 于是这次我看到 5000 端口在 * 上面被 Control Center 占用了, 127.0.0.1 的5000端口, 被 Flask app 占用了:

eric@host % sudo lsof -i -n -P | grep 5000
ControlCe  9926         eric    7u  IPv4 0xbe79901362e29b39      0t0    TCP *:5000 (LISTEN)
ControlCe  9926         eric    8u  IPv6 0xbe799009db4de1b1      0t0    TCP *:5000 (LISTEN)
Python     9948         eric    8u  IPv4 0xbe799013679bab39      0t0    TCP 127.0.0.1:5000 (LISTEN)

解决办法

顺着上面找到的关键字, 我们Google 一下, 第一个就是这个官方问答: https://developer.apple.com/forums/thread/682332
原来是 Control Center 里的 AirPlay Receiver 监听在这个端口.
AirDrop___Handoff.png

勾掉这个, 就发现不在监听 5000 端口了:

eric@host % sudo lsof -i -n -P | grep 5000
Python     9948         eric    8u  IPv4 0xbe799013679bab39      0t0    TCP 127.0.0.1:5000 (LISTEN)

当然, 从另外一个角度, 我们只要改改 Flask 的默认端口, 也能达到这个效果:

_app, _socketio = create_app()
_socketio.run(_app, host='0.0.0.0', port=5050)

30天休假-day30

本来打算今天去运河博物馆的, 可是没有预约到, 只能作罢. 另外一个选择是去扬州的一个动物园, 或者去无锡的一个景点. 最后因为有人不舒服, 直接选择开车回家.

从扬州开车回家需要4个小时, 大概360多公里, 车大概还有390多公里. 本来想去梅村服务区充电, 结果发现那里的电车都排队充电, 所以最后一路开回家.

路上看到发生至少2个车祸, 后来进入上海后堵的不行. 估计是之前1周多都是下雨, 今天终于放晴, 路上车特别多. 到家都2点20了.

然后搬东西回家, 收拾家里. 下午再去郊野公园充个电.

30天假期 结束.

30天休假-day29

早上醒来8点多, 然后洗漱, 吃饭, 小孩终于吃到了自己喜欢的饭团.

然后出发去扬州. 第一站就是去瘦西湖, 人是真多, 里面到处都是外地人. 其实拍照拍不出来啥效果. 中午选了一家本地特色的饭店, 里面的菜真不错, 应该是这一路最符合我们口味的菜了.

也许就是那句 故人西辞黄鹤楼, 烟花三月下扬州. 导致扬州的酒店特别贵. 游玩的同时就开始看酒店, 发现稍微便宜点酒店很快就被订完了. 最后定了一个距离瘦西湖20 多公里多朵亚.

下午随便转转, 然后晚上有烟花表演, 表演都一般, 投到白塔多那个很一般, 西门门口的喷泉还不错. 最后定无人机表演也不错.

从园区出来, 然后去麦当劳买了汉堡, 因为在里面吃的东西都消化完了.

晚上赶到酒店都快10点了. 然后查第二天想去的 运河博物馆, 结果发现这个不要门票, 但是需要预约, 明天周日的都被预约完了. 不知道明天要去哪里了. 剩下2个私家园林, 不想去, 因为真多看不懂, 就类似去苏州的拙政园.

30天休假-day28

今天一早起来去接小孩外公出院, 早上六点半起床. 然后出发去县中医院. 到那里还不到8点.

然后等医生, 护士上班. 他们上班还挺早, 不到8点, 人都很多了.

然后回家. 在家没事带小孩去地里看他年底时候做的水坝, 结果那水坝还在. 说明现在几乎没有小孩去地里玩了.

晚上带小孩去澡堂洗澡.