中国浙江温州小型皮鞋厂老板,面对皮鞋进水发胖问题,没有任何解决的办法!

本周末写点轻松的好玩的事,所谓轻松的好玩的事,那就是既不怕犯错,又不用背锅的事,完全不用负责任,那就说说telnet吧。

如题目,这个问题曾经困扰了我好多年,初识这个问题是在2003年,那时的老师也没有讲清楚。后来我想清楚了,但一直没有时间总结,好像发过朋友圈,但真的是忘了。

早上班车上刷到一篇相关的知乎问题挺有意思:
https://www.zhihu.com/question/22733033
<https://www.zhihu.com/question/22733033>
不过仅有的几个回答都没有回答到点子上,包括最长的那个也只是介绍了下telnet而已。倒是提问者自己解释的比较到位:

如果我来回答,我会说 “是的,你的理解很对!”

但本人比较讨厌知乎的那种带着怼天怼地的嘲讽,好多没文化装懂强答的互动氛围,于是基本都是看笑话潜水不参与的状态。还是记录到这里吧。

我最初对这个问题的疑问的缘起是这样的。

我们大专生在学习网络课程的第一课时,老师就讲了 telnet是一个远程登录协议
,紧接着老师就演示了telnet如何工作。毕竟我们大专生的课程是以实践为主线。但本科网络教材一般不会这么讲的,本科教材一般肯定是撸OSI/RM。

嗯,我第一次接触网络的时候,知道了 “telnet是一个远程登录协议” 这么一个结论。

第二节课我们大专生就开始直接操作TCP/IP了,牛逼了。这节课过后,还是这位老师,告诉我们 “使用telnet可以测试端口通不通”
。有点和第一节课的结论矛盾了…

一个拥有自己特定端口的应用层协议,怎么可以用来测试别的协议端口通不通???


后面我们紧接着学习了HTTP,DNS,FTP等协议,越学越懵…看谢希仁的《计算机网络》本科教材是不会有这种懵逼的感觉的,所以很少有本科生,研究生去纠结这个问题。

不纠结不代表彻底理解。

我是后来工作实践中发现大专两年学的那些操作性的东西完全不够,又自学的本科,研究生教材,才解开这个看似非常简单的疑问。

如果telnet是一个协议,那么它大概和HTTP是并列的,只不过HTTP使用80端口来标示,而telnet使用22端口罢了。这无可厚非。但是事实还有另一面。

所有的应用层协议端口标示都只是一个默认端口而已。

HTTP并不是一定要用80端口,SSH也并不一定要用22端口,任何使用这些协议的软件都会提供一个端口配置参数让你可以修改端口,比如Apache
tomcat默认就使用8080端口来标示Web服务,当然,你必须在浏览器里显式输入8080端口:

同理,ssh登录也允许你使用不同于22的其它端口:


我们之所以可以用非80端口访问Web服务,可以使用非22端口远程ssh登录,完全是因为在这些服务器上显式配置了这些非默认的端口,比如上面我用60022登录了ssh,那是因为我修改了sshd的配置文件并且重启了服务:


不管你把端口改成什么,只要对应客户端访问了该端口,那么得到就是对应的服务,你完全可以让Web服务器侦听22端口,让sshd使用80,然后浏览器里输入:22访问网页,ssh命令行加入-p
80来远程登录。

然而,你不能交叉访问服务,比如我用22访问Web:

你也不能用8080访问ssh:


如果不把telnet扯进来,这些都是很容易理解的。

单单看telnet这个命令,它长得就和ssh命令差不多,在TCP的C/S模型看来,它也和浏览器属于同一档位,但是telnet的表现和它们都不同。

telnet占用默认的23端口,这个道理和Web占用默认的80端口,ssh占用默认的22端口是一致的。

但是, 你几乎从来没有登录过23端口! 也就是说, 你几乎从来没有“不带端口参数”使用过telnet命令!

想想看是不是呢?

如果我使用ssh,携带了端口参数(-p
$port指示),上面讲了,那是因为我的服务器配置文件中将侦听端口改成了那个端口,如果不使用那个端口,便不通!那么对应到telnet,我们使用下面的命令:
[[email protected] ~]# telnet 192.168.56.110 8080
是不是意味着telnet服务器的侦听端口改成了8080,如果没有改成8080,这个访问就不通呢?

根本不是!


192.168.56.110上的8080端口是Tomcat的Web服务端口,根本不是telnet服务器的端口,这台机器上甚至没有安装telnet服务器程序!然而,回车键敲下去,还是通的:
[[email protected] ~]# telnet 192.168.56.110 8080 Trying 192.168.56.110...
Connected to 192.168.56.110. Escape character is'^]'.
并没有因为telnet服务器的配置不是8080而导致不通,它甚至可以解释HTTP!当我按照HTTP 1.X的规范输入get /并连续敲入回车(
注意,不是HTTP规范中的两个回车!)后,tomcat的报错html页面竟然返回了:
[[email protected] ~]# telnet 192.168.56.110 8080 Trying 192.168.56.110...
Connected to 192.168.56.110. Escape character is'^]'. get / <!doctype html><
html lang="en"><head><title>HTTP Status 405 – Method Not Allowed</title><style
type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;
background-color:#525D76;font-size:22px;} h2
{font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;}
h3
{font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;}
body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;}
b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p
{font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}
a {color:black;} a.name {color:black;} .line
{height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP
Status 405 – Method Not Allowed</h1><hr class="line" /><p><b>Type</b> Status
Report</p><p><b>Message</b> JSPs only permit GET POST or
HEAD</p><p><b>Description</b> The method received in the request-line is known
by the origin server but not supported by the target resource.</p><hr
class="line" /><h3>Apache Tomcat/8.5.37</h3></body></html>Connection closed by
foreign host.
如果你不敲入回车,让命令提示符仅仅停留在 Escape character is '^]'
的话,给人的感觉就是telnet命令接入了远端的telnet服务,给人的感觉就是远端的telnet服务的端口从23改成了8080,然而敲入回车之后,竟然返回了html。

远端根本就不是telnet服务器!然而竟然能通!

基于这一点, telnet沦(注意用词)为了“测试端口连通性”工具

哈哈,就像百度一样,成了IP层网络能不能连接外网的测试工具:

* 如果是普通用户刚刚装上了宽带,那就是浏览器看看能不能打开百度的主页。
* 如果是码农或者网管,就命令行输入ping www.baidu.com <http://www.baidu.com>,顺便查一下DNS。
但其实,人家百度是个搜索引擎啊!

我们对照着百度的角色,看看telnet:

* 为了check一下远端的某个TCP端口是否打开,就telnet一下,出现 Escape character is '^]' 的就说明打开了。
但其实,人家telnet是个跟ssh类似(虽然不那么安全!)的远程登录协议啊!


ssh登录非ssh服务的端口,ssh协议本身会做校验,发现不是ssh协议,就会直接断开,同理,如果你ssh一个Web服务的端口,HTTP服务器也会发现这不是一个HTTP请求而断开连接。可是…

可是,为什么telnet不做这种校验呢?

其实,这就是问题的根本,不是telnet不做校验,是telnet校验的不够完美。我们来看一下。

我在我的CentOS上启动telnet服务,打开默认端口23。我们来用ssh登录一下23端口:
[[email protected] ~]# ssh [email protected] -p 23 sss dddd aaa ^C
没有任何反应,但是数据还是交互的,这个抓包可以看出来:

虽然telnet服务器始终没有接受这次伪装的连接,但是也同样没有给出任何应答,哪怕是报错都没有! 这是真正的沉默者! 这样做是正确的!这样做是安全的!
不和陌生人说话 是安全首要原则。

我在想,这真的是telnet实现的不够完美还是说这是故意所为。我记得之前看过极端的UNIX拥护者说的UNIX哲学中有句是 沉默是金 。

到此为止,解答了疑问:

* telnet是一个协议,和HTTP,SSH这些类似,有完整的telnet交互规则。
* telnet是一个命令行工具,它用来连接telnet服务器,但是由于 某种原因 它也可以连接别的端口。
同样的事情,我们看看http和ssh的情况,来对比telnet。只要是基于TCP的服务,都会有这个问题。


http是一个协议,它的服务器一般都是httpd,nginx之类,我们暂且不管,但是它的客户端几乎没有叫http的,一般都是curl,wget,Chrome,IE,Firefox,Safari…

ssh是一个协议,它的服务器一般都是sshd,我们暂且不管,它的客户端,最基础的就是ssh了,然而常用的还有SecureCRT,iTerm之类。


telnet是一个协议,它的服务器一般叫做telnetd,我们暂且不管,它的客户端,常用的仅有一个,那就是telnet命令行工具,而且我们还几乎从来不用它来登录telnet!

如果我们把telnet改个名字,叫做nc岂不是更好?注意,这里的nc不是netcat,而是network
checking的缩写。如果我们把telnet命令行工具改个名字,就不会有疑问了。

看来,说来说去,就是因为取错了名字!那么看来要结束本文了。

如果就此结束,那不是我的风格。我想要看一下telnet的细节。

首先,通过抓包,我发现我执行telnet $host不携带端口,抓包显示在完成三次握手之后,客户端会主动发送一大堆控制字符。

我现在就来实验,本地开启telnet服务器,连接本地telnet服务:
[[email protected] ~]# telnet 127.0.0.1 Trying 127.0.0.1... Connected to
127.0.0.1. Escape character is'^]'. Kernel 3.10.0-xxxyyyzzz.x86_64 on an x86_64
localhost login:
完美的远程登录过程,都让我输入用户名密码了。但我关注点不在这里,我来看抓包:

但是如果我telnet 80端口,在三次握手之后,telnet客户端便不会主动发出那么一堆控制字符,而是在输出 Escape character is
'^]' 之后,等待在那里,彼此谁也不发送任何数据。

后来,我把机器的sshd服务也改成了23端口,发现只要是telnet不带端口参数连接默认的23端口,都会发送控制字符:
[telnet DO SUPPRESS GO AHEAD, WILL TERMINAL TYPE, WILL NAWS, WILL TSPEED, WILL
LFLOW, WILL LINEMODE, WILL NEW-ENVIRON, DO STATUS[|telnet]
为了确认这个事情,看一下telnet命令的manual。里面有提到,请仔细阅读下面一段:

核对下源码。

telnet的源码在inetutils包中,这是一个非常古老的玩意儿:
https://ftp.gnu.org/gnu/inetutils/ <https://ftp.gnu.org/gnu/inetutils/>
看到源码的实现和抓包是匹配的:

好了,皮鞋湿,不会胖!

原来,我们一直都是 利用了telnet的实现不完美的漏洞 来将其作为 TCP端口探测的工具 吗?真是搞不清楚telnet是不是故意的。


telnet在它出现的那个年代,它几乎是TCP协议C/S模式的唯一协议,那个时候还没有HTTP,也没有SSH,什么都没有,只有telnet,或许还有FTP。如果是你来设计这个协议的处理逻辑,你能做到什么程度的完备性呢?


不过可以肯定的是,telnet非常复杂,虽然它是一个古老的协议但是它并没有你想象的那样简单。并且,telnet是开创了远程登录信令的鼻祖泰斗级协议,千万别鄙视telnet!

有多少人熟悉telnet协议的那些控制信令呢?有多少人意识到这些设计对后世的影响呢?

希望更加详细地了解telnet,请看专门规范telnet地RFC854:
https://tools.ietf.org/html/rfc854 <https://tools.ietf.org/html/rfc854>

为什么会写这个文章,通篇没有任何的技术含量…


我想表达的就是一个思路,一个排查问题,分析问题,解决问题的思路。如果你希望能快速定位问题并解决问题,那就必然要拥有一种素养,那就是碰到任何哪怕再简单再无聊的问题都不能得过且过,必须刨根问底,事情的来龙去脉了然于胸,真相便自然掌握在手!

皮鞋湿,不会胖。

中国浙江温州小型皮鞋厂老板,面对皮鞋进水发胖问题,没有任何解决的办法!