杰瑞科技汇

Python telnetlib 登录如何实现?

telnetlib 简介

telnetlib 是 Python 标准库的一部分,它提供了一个 Telnet 客户端,使用它,你可以编写 Python 脚本来连接到 Telnet 服务器,并自动执行命令。

Python telnetlib 登录如何实现?-图1
(图片来源网络,侵删)

基本连接和登录流程

一个典型的 Telnet 自动登录流程如下:

  1. 导入 telnetlib 模块
  2. 创建 Telnet 对象并连接到服务器,指定主机名/IP 地址和端口号(默认为 23)。
  3. 读取服务器返回的初始信息(如欢迎信息或登录提示)。
  4. 发送用户名
  5. 读取服务器返回的密码提示
  6. 发送密码
  7. 登录成功后,发送命令
  8. 读取命令的输出
  9. 关闭连接

关键方法

  • telnetlib.Telnet(host, port=23): 创建一个 Telnet 对象并尝试连接。
  • read_until(expected_string, timeout=None): 读取数据,直到遇到 expected_string 或超时,这对于等待登录提示非常有用。
  • read_all(): 读取缓冲区中所有的剩余数据。
  • write(buffer): 向服务器发送数据。注意:发送的数据需要是字节串 (bytes),所以字符串通常需要用 .encode('ascii').encode('utf-8') 转换。
  • expect(list_of_expected_strings, timeout=None): 这是一个更强大的方法,它会读取数据,直到匹配到 list_of_expected_strings 中的任何一个,并返回一个元组 (index, match, bytes_read)index 是匹配到的字符串在列表中的索引,非常适合处理 "Login:" 和 "Password:" 这两种不同的提示。
  • close(): 关闭 Telnet 连接。

示例代码

示例 1:简单直接的登录

这个例子假设服务器的登录流程非常简单:先收到 "login:",然后收到 "password:"。

import telnetlib
import time
# --- 配置 ---
HOST = "your_telnet_server_ip"  # 替换为你的 Telnet 服务器 IP
PORT = 23                       # 默认 Telnet 端口
USER = "your_username"          # 替换为你的用户名
PASSWORD = "your_password"      # 替换为你的密码
TIMEOUT = 10                    # 设置超时时间,单位:秒
try:
    # 1. 连接到 Telnet 服务器
    print(f"正在连接到 {HOST}...")
    tn = telnetlib.Telnet(HOST, PORT, timeout=TIMEOUT)
    # 2. 读取初始信息,直到出现 "login:" 提示
    #    注意:有些服务器可能提示 "Username:" 或 "Login:"
    tn.read_until(b"login: ", timeout=TIMEOUT)
    print("已收到 'login:' 提示。")
    # 3. 发送用户名
    #    .encode('ascii') 将字符串转换为字节串
    tn.write(USER.encode('ascii') + b"\n")
    print(f"已发送用户名: {USER}")
    # 4. 读取密码提示 "password:"
    tn.read_until(b"password: ", timeout=TIMEOUT)
    print("已收到 'password:' 提示。")
    # 5. 发送密码
    tn.write(PASSWORD.encode('ascii') + b"\n")
    print("已发送密码。")
    # 6. 等待登录成功,可以读取一个命令提示符,'$', '>', '#' 等
    #    这里我们简单等待几秒,让系统稳定
    time.sleep(2) 
    # 读取登录后的输出,清空缓冲区
    output = tn.read_very_eager()
    print("登录成功!")
    print("服务器返回信息:", output.decode('ascii'))
    # 7. 发送一个命令,'ls -l'
    print("\n正在执行命令: 'ls -l'")
    tn.write(b"ls -l\n")  # 命令后也要加换行符 \n
    # 8. 读取命令的输出,直到再次看到命令提示符
    #    这是一个更健壮的方法,等待出现 '$' 或 '>' 或 '#'
    tn.read_until(b"$ ", timeout=TIMEOUT) # 假设提示符是 '$ '
    output = tn.read_very_eager()
    print("命令 'ls -l' 的输出:")
    print(output.decode('ascii'))
except ConnectionRefusedError:
    print(f"错误: 无法连接到 {HOST}:{PORT},服务器可能未运行或地址错误。")
except EOFError:
    print("错误: 连接被服务器意外关闭。")
except Exception as e:
    print(f"发生未知错误: {e}")
finally:
    # 9. 确保连接被关闭
    if 'tn' in locals() and tn.get_socket() is not None:
        tn.close()
        print("\n连接已关闭。")

示例 2:使用 expect() 的更健壮方法

expect() 方法比 read_until() 更灵活,因为它可以等待多个可能的提示,这在处理不同提示符(如 "Login:" vs "Username:")或登录失败时非常有用。

import telnetlib
# --- 配置 ---
HOST = "your_telnet_server_ip"
PORT = 23
USER = "your_username"
PASSWORD = "your_password"
TIMEOUT = 10
try:
    print(f"正在连接到 {HOST}...")
    tn = telnetlib.Telnet(HOST, PORT, timeout=TIMEOUT)
    # 定义我们期望的提示符列表
    # 匹配顺序很重要,会按顺序检查
    login_prompts = [b"login: ", b"Username: ", b"Login: "]
    password_prompt = b"password: "
    shell_prompt = b"$ " # 假设命令行提示符是 $
    # 1. 等待用户名提示
    #    expect 返回 (匹配的索引, 匹配的对象, 读取到的字节)
    index, match, text = tn.expect(login_prompts, timeout=TIMEOUT)
    if index == -1:
        raise Exception("未收到预期的用户名提示。")
    print(f"已收到用户名提示: {match.group().decode('ascii')}")
    # 2. 发送用户名
    tn.write(USER.encode('ascii') + b"\n")
    print(f"已发送用户名: {USER}")
    # 3. 等待密码提示
    index, match, text = tn.expect([password_prompt], timeout=TIMEOUT)
    if index == -1:
        raise Exception("未收到预期的密码提示。")
    print(f"已收到密码提示: {match.group().decode('ascii')}")
    # 4. 发送密码
    tn.write(PASSWORD.encode('ascii') + b"\n")
    print("已发送密码。")
    # 5. 等待登录成功,出现命令行提示符
    #    在真实环境中,登录过程可能会输出一些信息,所以用 expect 来捕获提示符
    index, match, text = tn.expect([shell_prompt, b"Login incorrect", b"Permission denied"], timeout=TIMEOUT)
    if match.group() == b"Login incorrect" or match.group() == b"Permission denied":
        raise Exception("登录失败:用户名或密码错误。")
    print("登录成功!")
    # 6. 发送命令并读取输出
    command = "hostname"
    print(f"\n正在执行命令: '{command}'")
    tn.write(command.encode('ascii') + b"\n")
    # 等待命令执行完毕并再次看到提示符
    tn.expect([shell_prompt], timeout=TIMEOUT)
    # 读取命令的输出
    output = tn.read_very_eager()
    print(f"命令 '{command}' 的输出:")
    print(output.decode('ascii'))
except Exception as e:
    print(f"发生错误: {e}")
finally:
    if 'tn' in locals() and tn.get_socket() is not None:
        tn.close()
        print("\n连接已关闭。")

重要注意事项和最佳实践

  1. 安全性Telnet 协议是明文传输的,这意味着你的用户名、密码和所有命令都会以明文形式在网络中传输。强烈建议不要在生产环境或任何不信任的网络中使用 Telnet,对于需要远程管理的场景,请优先使用 SSH(可以通过 paramiko 库实现)。

    Python telnetlib 登录如何实现?-图2
    (图片来源网络,侵删)
  2. 编码问题

    • telnetlib 处理的是字节流,当你从服务器读取数据时,得到的是 bytes 对象,你需要使用 .decode('ascii').decode('utf-8', 'ignore') 将其转换为字符串。'ignore' 可以忽略无法解码的字符,避免程序崩溃。
    • 向服务器发送数据时,字符串也需要用 .encode() 转换为 bytes
  3. 超时设置timeout 参数非常重要,如果服务器没有响应,你的脚本可能会无限期地等待下去,为所有可能阻塞的操作(如 read_until, expect)设置一个合理的超时值。

  4. 提示符的多样性:不同操作系统(Linux, Cisco IOS, Juniper Junos 等)的提示符可能不同,登录成功后的提示符可能是 , >, 或自定义的字符串,你需要根据你的目标设备调整等待的提示符。

  5. 处理交互式命令:对于 more/less 这样的分页命令,telnetlib 无法直接处理,你需要先发送命令(如 set pager 0)来禁用分页,或者模拟按键(如空格键或 q 键)来退出分页界面。

  6. 调试:如果脚本行为不符合预期,可以添加打印语句来查看服务器返回的原始字节流,这有助于你确定正确的提示符和流程。

    # 在读取数据后打印
    raw_data = tn.read_until(b"login: ")
    print(f"原始接收数据: {raw_data}")
分享:
扫描分享到社交APP
上一篇
下一篇