2018年11月1日 星期四

Python 學習筆記 : telnetlib 模組測試

最近接到上級交付一個自動化任務, 要寫個程式用 telnet 方式連線四個主機, 對其下達指令後讀取回應資料, 剖析後自動做成執行結果報告. 我打算揚棄網頁實作的老方法, 改用 Python 來實現, 順便把 Python 的 tkinter GUI 模組練起來.

Python 內建標準函式庫中的 telnetlib 實作了 RS854 Telnet 協定, 參考 :

https://docs.python.org/3/library/telnetlib.html#module-telnetlib

模組 telnetlib 不需另外安裝, 直接匯入其中的 Telnet 模組使用 :

from telnetlib import Telnet 

呼叫 Telnet(addr [, port]) 建構函數會傳回 Telnet 物件, 參數 port 若未傳入則為 Telnet 協定之預設埠 23, 但有些主機不使用 23 而是指定特定的 Telnet 埠, 例如 :

tn=Telnet("192.168.0.16")              #預設 port 23
tn=Telnet("192.168.0.16", 2316)    #指定 port 2316

然後就可以呼叫下列 Telnet 物件常用的方法進行通訊, 主要是呼叫 write() 方法傳送指令, 以及呼叫 read_all(), read_until(), read_eager(), read_very_eager() 等讀取回應資料.

 Telnet 物件的方法 說明
 open(host, port=23[, timeout]) 阻塞式開啟主機 host 的 Telnet 連線, 直到 timeout 秒逾時
 write(cmd) 將 byte 字串 cmd 寫入輸出串流
 read_all() 阻塞式讀取全部回應串流直到檔尾 (EOF), 傳回 byte 字串
 read_until(expected[, timeout) 阻塞式讀取串流直到期待之 byte 字串 expected 出現或 timeout 秒數逾時, 傳回 byte 字串
 read_very_eager() 非阻塞式讀取全部可讀到之串流, 傳回 byte 字串
 read_eager() 非阻塞式讀取串流中已收到之資料, 傳回 byte 字串
 read_very_lazy() 非阻塞式讀取 queue 中全部資料, 傳回 byte 字串
 read_lazy() 非阻塞式讀取 queue 中已有之資料, 傳回 byte 字串
 expect(reg_list, timeout=None) 阻塞式讀取全部回應串流直到串列 reg_list 中的正規式有一個符合為止, 傳回三元素之 tuple
 interact() 模擬 Telnet 客戶端, 與主機進行互動式通訊
 close() 關閉 Telnet 連線

用法範例參考 :

telnetlib python example


每一種主機 Telnet 互動方式不同, 因此需客製化處理, 例如 :


測試 1 : A 主機使用 read_until()

from telnetlib import Telnet
tn=Telnet("192.168.0.16")
tn.read_until(b"Enter User Name")
tn.write("tabget1".encode('ascii') + b"\r\n")
tn.read_until(b"Enter Password")
tn.write("123456".encode('ascii') + b"\r\n")
tn.write("get table users".encode('ascii') + b"\r\n")
tn.write("lis all(2==sales)".encode('ascii') + b"\r\n")
print(tn.read_until(b"BOTTOM").decode('ascii'))
tn.write("lis all(2==managers)".encode('ascii') + b"\r\n")
ret=tn.read_until(b"BOTTOM").decode('ascii')
print(type(ret))
print(cos)


注意, 函數 read_until(), read_all() 是阻塞式 I/O, 即程式會停住等待指定字串或 EOF 字元的到來, 若未收到 EOF 程式就當在那邊, 解決辦法是改用 read_eager() 與 read_very_eager() 等非阻塞式 I/O 函數, 但若資料較多, 還沒全部被抓進緩衝器, 此函數就回傳了, 這樣可能會抓到最前面的部分資料. 解決辦法是使用 time.delay() 來拖一下時間即可, 參考 :

telnet读取返回数据小心得


測試 2 : B主機使用 read_very_eager() 

import time
from telnetlib import Telnet
tn=Telnet("192.168.70.11", 2361)
tn.write(b"\x1b")
tn.write("LOGIN::tony:::blablabla".encode('ascii')+b"\r\n")
tn.write("LIST-ROUTES::GOOGLE;".encode('ascii')+b"\r\n")
time.delay(3)
ret=tn.read_very_eager().decode('ascii')
print(ret)
tn.write("BYE;".encode('ascii')+b"\r\n")

此例中的主機在連線成功後必須先送一個 ESC 鍵再登入, ESC 的 ASCII 碼為 \xib :

telnetlib.Telnet.write("\x1b")  #傳送 ESC 鍵

參考 :

How to send the Esc key viaa telnetlib.Telnetsession?


Windows 本身有 Telnet 伺服器, 但在 Win10 預設是關閉的, 需到 "控制台/程式和功能/開啟或關閉 Windows 功能" 開啟 "Telnet 用戶端", 例如 :






這樣便可使用命令提示字元視窗連線本機 Telnet 伺服器了.

Telnet 指令如下 :

C: Telnet 進入 MS Telnet Client
Microsoft Telnet>

輸入 >help 會列出指令集說明, 常用指令如下 :

1. set 指令
>set localecho  (開啟本地回應, 才會顯示鍵入指令, 預設關閉)
>set logfile d: emp est.log (設定日誌檔)
>set term vt100 (設定終端機型式)
其他關於 set 之參數可下 set ? 查詢

2. open 指令
>open 127.0.0.1 23
如果沒有指令 port 則預設為 port 23

3. close 指令
>close (關閉目前連線)

4. quit 指令
>quit (結束 MS Telnet, 回到 CMD)

5. display 指令
>display (顯示目前設定)
Microsoft Telnet> display
逸出字元是 'CTRL+]'
會驗證(NTLM 驗證)
本機回應關閉
換行模式 - 導致 return 鍵傳送 CR & LF
目前的模式: 主控台
Will term type
喜好的終端機類型為 ANSI

6. send 指令
>send brk (傳送 telnet 指令 break 給伺服器)
>send ? (顯示可用指令)
ao      傳送 telnet 命令 'Abort Output'
ayt     傳送 telnet 命令 'Are You There'
brk     傳送 telnet 命令 brk
esc     傳送目前的 telnet 逸出字元
ip      傳送 telnet 命令 'Interrupt Process'
synch   傳送 telnet 命令 synch
任何其他字串將以原字串傳送給 telnet 伺服器


參考 :

Using Telnet in Python
https://cloud.tencent.com/developer/section/1368557
telnetlib python example
[python]利用telnetlib登入ptt
[Python] BBS Crawler 筆記
python3+ros+telnet+telnetlib
Python 3, telnet automation
用python 脚本控制telnet登录交换机
python3 telnetlib模块,关于字节编码这些需要怎么处理,全是乱的怎么回事?

沒有留言 :