【发布时间】:2017-06-09 05:23:55
【问题描述】:
多年来,我一直在特定程序中使用 IdMappedPortTCP 来允许通用端口转发。我正在测试升级的构建/组件环境,但遇到了问题。首先,这是新旧版本信息:
- 操作系统:W2kSP4 --> 相同(嘿,为什么大家都在笑?)
- 德尔福:5 --> 7
- Indy 项目:9.0.0.14 --> 9.[最新 SVN]
我正在使用标准 Windows 控制台 telnet 客户端和 Linux 服务器将其插入 telnet 会话中对其进行测试,我发现行为发生了奇怪的变化。
- 直接连接:客户端连接,立即看到服务器问候
- Old Indy:与直接相同
- New Indy:客户端连接,什么也看不见。按键,看到服务器问候+击键。
下面是事件链的对比:
旧:
6/08/2017 6:47:16 PM - DEBUG: MappedPort-Connect
6/08/2017 6:47:16 PM - TCP Port Fwd: Connect: 127.0.0.1:4325 --> 127.0.0.1:23
6/08/2017 6:47:16 PM - DEBUG: MappedPort-OutboundConnect
6/08/2017 6:47:16 PM - TCP Port Fwd: Outbound Connect: 192.168.214.11:4326 --> 192.168.210.101:23
6/08/2017 6:47:16 PM - DEBUG: MappedPort-OutboundData
6/08/2017 6:47:16 PM - DEBUG: MappedPort-Execute
6/08/2017 6:47:16 PM - DEBUG: MappedPort-OutboundData
6/08/2017 6:47:16 PM - DEBUG: MappedPort-Execute
6/08/2017 6:47:16 PM - DEBUG: MappedPort-OutboundData
...
新功能:
6/08/2017 6:41:34 PM - DEBUG: MappedPort-Connect
6/08/2017 6:41:34 PM - TCP Port Fwd: Connect: 127.0.0.1:1085 --> 127.0.0.1:23
6/08/2017 6:41:34 PM - DEBUG: MappedPort-OutboundConnect
6/08/2017 6:41:34 PM - TCP Port Fwd: Outbound Connect: 192.168.214.59:1086 --> 192.168.210.101:23
6/08/2017 6:47:36 PM - DEBUG: MappedPort-Execute
6/08/2017 6:47:36 PM - DEBUG: MappedPort-OutboundData
6/08/2017 6:47:36 PM - DEBUG: MappedPort-Execute
6/08/2017 6:47:36 PM - DEBUG: MappedPort-OutboundData
6/08/2017 6:47:36 PM - DEBUG: MappedPort-Execute
在第一个中,您在连接后立即看到 OutboundData。在第二个中,连接后什么都没有发生,直到我发送了一个击键(6 分钟后),此时您会看到 Execute,然后是第一个 OutboundData 事件。
这让我疑惑:是真的连接到服务器只延迟了输出,还是连接本身延迟了?
我的第一个结论是连接本身被延迟,这就是原因。服务器在登录提示符处有 1 分钟的超时。如果您连接并收到问候但只是坐在那里,服务器会在一分钟后断开连接。使用新的 Indy 版本,我在连接事件后坐了整整 6 分钟,然后毫无问题地得到服务器问候。
但是... NETSTAT 显示在记录连接事件后不久建立的与远程服务器的连接!所以,我只能得出这样的结论:确实建立了连接,但也许某些初始字符正在被“吃掉”,或者导致 getty 在击键之前无法参与?
有什么建议吗?您是否知道我可能会寻找的任何改变——我应该做但没有做的事情?任何见解都值得赞赏。
(除非有任何好的线索,我猜我侦查的下一步可能是用 WireShark 嗅探两台机器,看看连接后发生了什么。)
更新:Wireshark(单腿)
从机器外部捕获的数据包显示 MappedPort 和服务器之间的流量(但不是客户端和 MappedPort 之间的流量)显示 telnet 服务器发送“Do Authenticate”,客户端(通过 MappedPort)回复 w/一个“将认证”。接下来是服务器发送验证子选项(并且客户端同意),然后是所有其他 telnet 选项。最后,在看到登录文本后,客户端发送“do echo”,他们都坐在那里直到 1 分钟后,此时服务器发送 TCP FIN 以关闭连接。那是“好旧”的版本。
在新版本中,客户端不响应“Will Authenticate”,并且他们都无限期地坐在那里。 (嗯,我想知道这在服务器资源方面有什么关系——可能是很好的 DOS 攻击。不过,它是一个旧的 telnet 守护进程,所以它现在可能已经修复了......)我终于发送了第一个按键,这就是它在那个数据包中发送的全部内容。然后客户端发送“将进行身份验证”(无需来自服务器的额外刺激),协商继续正常进行;来自服务器的最后一个数据包(包含回显参数)也包括输入的回显字符。因此,就好像客户端没有看到来自服务器的初始“进行身份验证”数据包,但是一旦您开始输入,就会继续并做出响应,就好像它刚刚听到它一样(一旦它发送了击键)。
6/13 更新:Wireshark(双腿)
我捕获了“断断续续”的对话的两条腿并对其进行了分析。有趣的行为。底线: 一旦服务器获得 TCP 连接,它就会发回 Telnet-DoAuth 邀请。 IdMappedPortTCP 会保留该数据包,但不会将其传递给客户端——目前还没有。一旦客户端最终发送第一次击键(几秒钟或几分钟后),Id 将其传递给服务器。 那么 Id 将从服务器获得的 DoAuth 数据包传递到客户端。
以下是对数据包的更详细统计:
65 11-59 TCP Syn
67 59-11 TCP SynAck
69 11-59 TCP Ack
71 59-101 TCP Syn
73 101-59 TCP SynAck
74 59-101 TCP Ack
76 101-59 DoAuth
77 59-101 TCP Ack
nothing for 23 seconds
79 11-59 Data:\r\n (I pressed Enter)
81 59-101 Data:\r\n
83 59-11 DoAuth
85 11-59 WillAuth
87 101-59 TCP Ack
88 59-101 WillAuth
90 101-59 TCP Ack
91 101-59 Authentication option
92 59-11 Authentication option
94 11-59 Authentication option reply
96 59-101 Authentication option reply
98 101-59 Will/do Encryption/terminal/env options
99 59-101 Will/do Encryption/terminal/env options
101 11-59 Don't encrypt
103 59-101 Don't encrypt
105 101-59 TCP Ack
106 59-11 TCP Ack
108 11-59 Won't/will list
110 59-101 Won't/will list
112 101-59 TCP Ack
113 101-59 Do window size
114 59-11 Do window size
数据包转储行格式: Pkt# From-To Payload
(不要介意数据包#跳过;客户端和代理都在运行捕获的机器托管的 VM 上运行,因此 Wireshark 看到了两个数据包副本。我只包括了 pkt# 所以我如果我愿意,可以稍后参考原始转储。)
从/到机器:
10 = Linux client (see below)
11 = Windows client
59 = proxy
101 = server
一个有趣的转移:Linux 客户端
虽然我的所有测试都使用了各种 Windows 客户端(因为那是生产中使用的),但我“意外地”使用了 Linux(因为那是我在我的工作站上运行的,我运行 Wireshark),因为它很方便。该客户的行为不同 - 更积极 - 从而避免了问题。这是一个转储的样子:
1 10-59 TCP Syn
2 59-10 TCP SynAck
3 10 59 TCP Ack
4 10-59 Do/Will list
5 59-101 TCP Syn
7 101-59 TCP SynAck
8 59-101 TCP Ack
10 59-101 Do/Will list
12 101-59 TCP Ack
13 101-59 DoAuth
14 59-10 DoAuth
15 10-59 TCP Auth
16 10-59 WontAuth
17 59-101 WontAuth
19 101-59 Will/Do list
20 59-10 Will/Do list
21 10-50 Do window size
22 59-101 Do window size
如您所见,客户端不会等待 telnet 服务器先说话——只要 TCP 连接建立,它就会发送一个完整的 Do/Will 列表。一旦 Id 打开该连接,这又会传递到服务器。服务器发回与之前最初所做的相同的“DoAuth”;不同之处在于,这一次,已经传递了来自客户端的流量,Id 立即传递它。然后客户端发送身份验证标志,事情就会继续进行。
所以,如果客户端先说话,IdMappedPortTCP 就可以了;只有当服务器先说话时,它才会保留它的消息,并且在客户端说什么之前不会将其传递给客户端。
9/27 更新:发现代码更改
降级到 9.0.0.14 解决了这个问题。对比两个版本的IdMappedPortTCP.pas源代码,我发现唯一的区别是新版本在procedure TIdMappedPortThread.OutboundConnect中增加了一段代码:
DoOutboundClientConnect(Self);
FNetData := Connection.CurrentReadBuffer;
if Length(FNetData) > 0 then begin
DoLocalClientData(Self);
FOutboundClient.Write(FNetData);
end;//if
except
(第一行和最后一行已经存在,仅显示上下文。)
我确认将该代码添加到 9.0.0.14 会产生问题。
我检查了 SVN 存储库,您在 2008 年 9 月 7 日添加了违规代码。提交评论是:
更新了 TIdMappedPortThread.OutboundConnect() 以检查待处理 OnOutboundConnect 之后入站客户端的 InputBuffer 中的数据 事件处理程序退出。
我不完全理解更改的原因或影响——显然你有充分的理由这样做——但它似乎确实产生了我描述的效果(“保持”服务器的初始输出直到客户端发送一些东西)。
【问题讨论】:
-
TIdMappedPortTCP的行为没有改变。它必须是服务器端的更改。特别是如果服务器问候被延迟到客户端发送数据之前。TIdMappedPortTCP对这种行为没有影响,因为它只是监视套接字状态。使用数据包嗅探器(如 Wireshark)验证行为。或者直接telnet到服务器。 -
谢谢,雷米。我希望你会出现!很高兴知道组件中没有已知的更改。不是服务器更改,因为 (1) 相同的行为更改发生在具有不同操作系统类型和版本的 6 台不同服务器上,以及 (2) 我正在针对同一台服务器同时测试两个版本。如果它不是 Indy 代码...我知道的其他不同之处是用于构建它的 Delphi 版本,只是不同的构建机器。一旦我对两条腿(client-fwd-server)进行同步数据包跟踪,我肯定会学到更多,希望是今天或周一。
-
如果不捕获客户端和
TIdMappedPortTCP之间的关系,就很难诊断出问题是在客户端本身还是在TIdMappedPortTCP。附带说明一下,如果您升级到 Indy 10,它有一个特定于 Telnet 连接的TIdMappedTelnet组件。 Indy 9 没有。尽管TIdMappedPortTCP应该可以正常工作。 -
从“6/13 更新:Wireshark(双腿)”开始添加更多信息。关于缩小延迟事件原因的最佳方法的想法?这就像第一个“从服务器获取数据”事件在从客户端获取数据之前不会触发。显然,从最后一句话的措辞来看,我对 Indy 的内部运作并不熟悉。 :-) 不知道如何才能更好地确定应该发生的事情。
-
原来 Indy 9 也有
TIdMappedTelnet。你试过了吗?无论如何,您所描述的不是TIdMappedPortTCP的运作方式。它不会保留等待客户端首先发送数据的服务器数据。两个连接(客户端到 Indy,Indy 到服务器)在单个线程循环中同时监控。在每次迭代中,如果客户端发送了任何数据,则将其发送到服务器,如果服务器已发送任何数据,则将其发送到客户端。他们不相互依赖。服务器可以先发送(例如问候和握手等)。