你的Linux上有一个超酷的 TCP 代理!

TL;DR 本文只用 Linux 自带的工具实现了一个可以将 TCP 流量打印出来的中间人代理程序(透明的)。脚本来源于这里。本文是对其工作原理的解释。

 

一般我们想知道一个 TCP 连接收发的内容时,会用 wireshark/tcpdump 这种工具,来把包抓下来分析查看。我开发 IRedis 的时候,就经常有这种需求,需要看看我的客户端对 server 到底发送了什么和接收了什么。用抓包工具不太方便,信息比较杂乱,操作也比较繁琐。在服务器上部署抓包工具的时候,还要给这些工具特殊权限。

其实这种需求可以不抓包的。因为 Redis 这种程序(RESP3协议)是基于 TCP 的,我们可以做一个代理,将发往 redis-server 的流量打印出来(输出到 stdout),然后再发给 redis-server。将 redis-server 的 Response 打印出来,再返回给客户端。相当于是一个中间人。

实现正向代理和反向代理都可以,反向代理不需要客户端支持,只是开一个端口,将所有发往这个端口的流量转发到 redis-server,所以我们这里用反向代理来实现。

在这篇文章中,我们只用 Linux 自带的工具,nc 来实现这个代理。还要用到 named pipe(因为 Linux 的 pipe 只支持单项传递,这里我们需要一个双向的代理),sed(用来对输出稍作格式化),trap (负责在退出的时候清理 named pipe)。

工作原理

我可以用一个最小化的版本解释这个中间人代理的工作原理,然后我们再来处理格式化输出。

这个中间人代理只需要两个 nc ,一个负责接收客户端那边的输出,将这个输入传给另一个 nc;另一个 nc 接收前一个 nc 的输入,然后传给 Server(比如 redis-server)。Server 传回来的信息也一样,先进入后一个 nc (后面称呼它为 nc2 好了),然后传给前一个 nc (后面叫做 nc1)。在 nc1 -> nc2 和 nc2 -> nc1 的过程中,就可以把传递的东西给打印出来了。

我们先写一个简单的版本:

这一行是监听 8800 端口,把收到的内容通过管道发给 nc2,然后 nc2 将内容发给 kawabangga.com 的 80 端口。其实就是一个 HTTP 代理啦。

用 curl 来试一下:

注意这个 curl 是卡住了,并没有信息返回。

而两个 nc 那一行的输出是:

可以看到,第二个 nc 把服务器的返回直接输出到 stdout 了,而并没有返回给 curl。这样的代理是不合格的,因为我们不想夺走本该属于 curl 的 response。

一个优秀的代理应该完整的将服务器的回复还给客户端(curl),像下面这样。至于 stdout 的问题,我们先不管。

Linux 的 pipe 只支持从一边传给另一边,是单向的。那怎么做到后面的进程往前面的进程传呢?答案就是 namedpipe。Named pipe,嗯……顾名思义,就是有名字的管道。在本文中,理解成有了这个名字,我就可以控制重定向的方向,就好了。

我们用这一行,就完全可以实现图2那种传输。即 nc2 通过 named pipe 传给 nc1 ,nc 默认会将自己的 stdin 的内容传回给连接到 nc 的 TCP 另一头。

用 curl 再来试一下:

而 nc 那个命令行没有任何输出。

中间人打印TCP内容

OK,代理已经工作了,接下来要做的是将 TCP 内容打印出来。这里要做的是 nc 在将内容输出到 stdout (即管道)的同时,还要打印出来。

……当然是要用 tee 啦!

这样可以用 tee 将 TCP 流量分别保存到 in.txt 和 out.txt 里面。可以用上面用过的 curl 测试一下。

因为 stdout 会被管道重定向,所以我们这里无法直接输出到屏幕了。那咋办呢?一不做二不休,再用 named pipe 将 tee 写入到 named pipe 中。

curl 之后,用 cat logging 就能看到 TCP 的内容啦。(这里如果不使用 cat 读出来 pipe 的内容,curl 会被 block 住)。

最终的脚本

最后,加点细节。写个脚本自动生成 named pipe,使用 trap 在退出的时候把创建的 named pipe 删掉。用 sed 格式化一下输出,分别加上 => 和 <=

 

代码如下(BSD 的 nc,适用于 OS X):

 

nc 的 BSD 版本和 GNU 版本不一样。如果你用的不是 Mac,要改下最后一行,加一个 -p. GNU 版本的代码如下:

 

使用效果如下(还是用 curl 测试):

 

作为 redis-server 的代理效果:

Pretty cool uh? Now I am going to drink some tea!

你的Linux上有一个超酷的 TCP 代理!”已经有2条评论

Leave a comment

电子邮件地址不会被公开。 必填项已用*标注