如何解码Telnet客户端收到的Cisco服务器返回响应字节串
问题解答
该响应对应的协议
你收到的固定字节串是Telnet协议标准定义的连接初始选项协商报文,不是Cisco设备专属返回内容,所有符合Telnet标准实现的服务端在TCP三次握手完成后,都会优先发送这类二进制控制报文协商传输参数,相关规则在Telnet核心规范中定义。
Telnet协议规定所有控制命令以0xff(即你看到的\xff,协议中称为IAC,Interpret As Command,命令转义标识)开头,后面紧跟命令类型和对应参数。逐段拆解你收到的字节含义:
\xff\xfb\x01:命令类型为WILL(服务端告知客户端「我将启用某功能」),对应选项码0x01,代表服务端将开启ECHO(输入回显)功能\xff\xfb\x03:命令类型为WILL,对应选项码0x03,代表服务端将开启Suppress Go Ahead(抑制半双工传输确认信号,切换为全双工传输模式)功能\xff\xfd\x18:命令类型为DO(服务端要求客户端「你需要开启某功能」),对应选项码0x18(十进制24),代表服务端要求客户端上报自身终端类型\xff\xfd\x1f:命令类型为DO,对应选项码0x1f(十进制31),代表服务端要求客户端支持终端窗口尺寸协商,窗口变化时主动上报尺寸参数
Telnet响应的正确解析方式
不要直接将收到的字节流转为字符串打印,Telnet连接初始阶段的字节流混合了二进制控制命令和普通文本内容,必须先拆分两类内容再分别处理:
- 逐字节遍历接收的数据流,遇到
0xff时判定为控制命令起始位 - 读取后续命令类型字节:如果是WILL/WON'T/DO/DON'T这四类常用协商命令,说明后续紧跟1字节的选项码,整条命令共3字节;如果是子协商类命令,按照对应选项的格式读取后续内容直到子协商结束标识
- 分离出的非命令字节才是可以直接解码的普通文本内容
- 对于协商命令需要按照协议规则回复对应响应,否则服务端会重复发送协商请求、不会进入正常的交互流程:如果同意服务端的WILL请求就回复DO,同意服务端的DO请求就回复WILL;不同意的话对应回复DONT/WONT即可。
可直接运行的解析示例代码
import socket # Telnet协议基础常量 IAC = b'\xff' # 命令起始标识 WILL = b'\xfb' # 本端将启用指定选项 WONT = b'\xfc' # 本端不启用/将关闭指定选项 DO = b'\xfd' # 要求对端启用指定选项 DONT = b'\xfe' # 要求对端不启用/关闭指定选项 # 常见选项码映射 OPT_NAME = { 1: "回显(ECHO)", 3: "抑制继续信号(SUPPRESS GO AHEAD)", 24: "终端类型(TERMINAL TYPE)", 31: "窗口尺寸协商(NAWS)" } def parse_telnet_stream(data): """ 解析Telnet接收字节流 返回值: (可直接解码的普通文本字节, 解析出的协商命令列表) """ text_seg = [] cmd_list = [] idx = 0 total_len = len(data) while idx < total_len: if data[idx:idx+1] == IAC: cmd_type = data[idx+1:idx+2] # 处理3字节长度的标准协商命令 if cmd_type in (WILL, WONT, DO, DONT): opt_code = data[idx+2] cmd_list.append( (cmd_type, opt_code) ) idx += 3 # 其他类型命令(含子协商)可按需扩展解析逻辑,此处暂跳过 else: idx += 2 else: text_seg.append(data[idx:idx+1]) idx += 1 return b''.join(text_seg), cmd_list # 测试逻辑 HOST = 'xxx.xxx.xxx.xxx' PORT = 23 s = socket.socket() s.connect((HOST, PORT)) raw_resp = s.recv(10000) text_content, cmds = parse_telnet_stream(raw_resp) # 打印解析出的协商命令 print("解析到服务端发送的协商命令:") for cmd_type, opt_code in cmds: cmd_name = {WILL:"WILL", WONT:"WONT", DO:"DO", DONT:"DONT"}[cmd_type] opt_name = OPT_NAME.get(opt_code, f"未知选项(0x{opt_code:02x})") print(f"- {cmd_name} {opt_name}") # 示例:全部同意服务端的协商请求,构造响应报文发送 reply_pkt = b'' for cmd_type, opt_code in cmds: if cmd_type == WILL: # 服务端告知要开启某选项,回复DO表示同意 reply_pkt += IAC + DO + bytes([opt_code]) elif cmd_type == DO: # 服务端要求客户端开启某选项,回复WILL表示同意 reply_pkt += IAC + WILL + bytes([opt_code]) s.sendall(reply_pkt)
内容的提问来源于stack exchange,提问作者smo zain
相关产品推荐
相关产品推荐

