LoginSignup
2
0

More than 5 years have passed since last update.

RubyのNet::TelnetはPromptを適切に設定しないと接続がTimeoutする

Posted at

はじめに

telnetは、汎用的な双方向通信プロトコルであり、平文で通信する。以前scpが失敗する件について記事にしたが、.bashrc等の設定次第で、RubyでNet::Telnetを使用するとき、失敗することがある。

Net::Telnetクラス

基本の使い方

Rubyのマニュアルを確認すると、

require 'net/telnet'

# リモートホスト "foobar" に接続
# タイムアウトは 10 秒
localhost = Net::Telnet.new("Host" => "localhost",
                            "Timeout" => 10)

# ログインし、プロンプトが出るまで待ち合わせる
telnet.login("your name", "your password") {|c| print c}

# ls コマンドを実行し、実行後、プロンプトが出るまで待ち合わせる
telnet.cmd("ls") {|c| print c}

# sleep で 5 秒
telnet.cmd("sleep 5 && echo foobar &") {|c| print c}

STDOUT.flush # <- これがないとここまで処理が来てることがわかりにくい

# 前のコマンドの出力を待ち合わせる
telnet.waitfor(/foobar\Z/) {|c| print c}

# ログインセッションの終了
telnet.cmd("exit") {|c| print c}
telnet.close

のように使用する。作成したtelnetインスタンスは、サーバから返ってきた平文を解釈するが、一番最後にプロンプト(例user @ server $)が表示されると、処理が終了したと認識し、次の処理に移れるようになる。

使用例

irb(main):001:0> require "net/telnet"
=> true
irb(main):002:0> pop = Net::Telnet.new("Host" => "192.168.179.9")
=> #<Net::Telnet:0x00007f8e159a59e8 @options={"Host"=>"192.168.179.9", "Port"=>23, "Prompt"=>/[$%#>] \z/n, "Timeout"=>10, "Waittime"=>0, "Binmode"=>false, "Telnetmode"=>true}, @telnet_option={"SGA"=>false, "BINARY"=>false}, @sock=#<TCPSocket:fd 9, AF_INET, 192.168.179.127, 62402>>
irb(main):003:0> pop.login("hikaru", "パスワード")
=> "\nKernel 3.10.0-957.1.3.el7.x86_64 on an x86_64\nClara login: hikaru\nPassword: \nLast login: Sun Jan 13 23:18:12 from ::ffff:192.168.179.127\n[hikaru@Clara ~]$ "
irb(main):004:0> pop.cmd("ls")
=> "ls\nDesktop    Downloads  Music\tPublic\t   Videos  hikaru\nDocuments  Dropbox    Pictures\tTemplates  cern    zabbix\n[hikaru@Clara ~]$ "
irb(main):005:0> exit

プロンプトの解釈について

Net::Telnetクラスでプロンプトの解釈は、クラスをnewするときにPrompt引数で指定でき、デフォルトだと"Prompt"=>/[$%#>] \z/nとなっている。プロンプトの書き換えは.bashrcなどでPS1=hogehogeの形で書き換えるが、ここで指定した形とNet::TelnetのPromptがアンマッチだと、処理が終了したと認識できず、Timeoutする。

再度Net::Telnetのデフォルトを確認すると、"Prompt"=>/[$%#>] \z/nのように、途中で半角スペースが必須となっている。そのため、サーバ側のPS1の書き換えで半角スペースを省いてしまうと、Net::TelnetがTimeoutする。

Timeoutする実行例

.bashrc@server
PS1="<\u @ \t>" # 最後に半角スペースなし
@Client
irb(main):001:0> require "net/telnet"
=> true
irb(main):002:0> pop = Net::Telnet.new("Host" => "192.168.179.9")
=> #<Net::Telnet:0x00007f9af40ae060 @options={"Host"=>"192.168.179.9", "Port"=>23, "Prompt"=>/[$%#>] \z/n, "Timeout"=>10, "Waittime"=>0, "Binmode"=>false, "Telnetmode"=>true}, @telnet_option={"SGA"=>false, "BINARY"=>false}, @sock=#<TCPSocket:fd 9, AF_INET, 192.168.179.127, 62401>>
irb(main):003:0> pop.login("hikaru", "パスワード")
Traceback (most recent call last):
        5: from /Users/hikaru/.rbenv/versions/2.5.0/bin/irb:11:in `<main>'
        4: from (irb):3
        3: from /Users/hikaru/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/net-telnet-0.2.0/lib/net/telnet.rb:748:in `login'
        2: from /Users/hikaru/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/net-telnet-0.2.0/lib/net/telnet.rb:696:in `cmd'
        1: from /Users/hikaru/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/net-telnet-0.2.0/lib/net/telnet.rb:557:in `waitfor'
Net::ReadTimeout (timed out while waiting for more data)

Timeoutしない例

.bashrc@server
PS1="<\u @ \t> " # 最後に半角スペースアリ
@client
irb(main):001:0> require "net/telnet"
=> true
irb(main):002:0> pop = Net::Telnet.new("Host" => "192.168.179.9")
=> #<Net::Telnet:0x00007fe15e1a8938 @options={"Host"=>"192.168.179.9", "Port"=>23, "Prompt"=>/[$%#>] \z/n, "Timeout"=>10, "Waittime"=>0, "Binmode"=>false, "Telnetmode"=>true}, @telnet_option={"SGA"=>false, "BINARY"=>false}, @sock=#<TCPSocket:fd 9, AF_INET, 192.168.179.127, 62469>>
irb(main):003:0> pop.login("hikaru", "パスワード")
=> "\nKernel 3.10.0-957.1.3.el7.x86_64 on an x86_64\nClara login: hikaru\nPassword: \nLast login: Sun Jan 13 23:18:59 from ::ffff:192.168.179.127\n<\xE6\x9C\xAC\xE7\x95\xAA hikaru 23:22:30> "

最後に

RubyのNet::Telnetを使用する場合は、上記のようにサーバ側のPS1を気にしないといけない。なお、普通のtelnetは違うルールでサーバのプロンプトを認識しているらしく、PS1の最後に半角スペースをいれなくても接続できた。

以上である。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0