Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2011年07月31日(日)

SSHサーバを作る (その1) サーバ

| 17:33 | SSHサーバを作る (その1) サーバ - Going My Ruby Way を含むブックマーク はてなブックマーク - SSHサーバを作る (その1) サーバ - Going My Ruby Way SSHサーバを作る (その1) サーバ - Going My Ruby Way のブックマークコメント

「挑戦: SSHサーバを作る」です。

まず、クライアントからの接続を受けつけコネクションを確立するサーバを作ろうと思います。

名前空間

Going My Ruby Way を略して GMRW(笑) をモジュール名にします。

GMRW の SSH2 の Server モジュールが SSHサーバです。

私の環境では $RUBYLIB に ~/lib/ruby/lib を切っているので、その下にスクリプトを置きます。

デフォルトポート

SSH サーバは標準でポート 22 を listen しますが、

  • 本物の sshd が使用中*1
  • root でないと使用できない*2

という理由から 50022 *3 をデフォルトポートとします。

gmrw/ssh2/server/constants.rb

#!/usr/bin/env ruby
# -*- coding : UTF-8 -*-

module GMRW
  module SSH2
    module Server
      DEFAULT_PORT = 50022
    end
  end
end

# vim:set ts=2 sw=2 et fenc=UTF-8:

SSH サーバのサービス

SSH プロトコルは 8ビット透過なリライアブルな転送路で動き、本来 TCP とは独立です。サービスは TCP と独立させて、転送路である TCPコネクションだけドライバからもらうようにします。

サービスを開始させるメソッドは start です。

gmrw/ssh2/server/service.rb (前半)

#!/usr/bin/env ruby
# -*- coding : UTF-8 -*-

require 'gmrw/ssh2/server/constants'

class GMRW::SSH2::Server::Service
  attr_reader :connection

  def initialize(conn)
    @connection = conn
  end

  def start
    # TODO: 具体的なサービス
  end
end

TCP サーバ部分は標準添付ライブラリの gserver を利用します。

gmrw/ssh2/server/service.rb (後半)

# 上の続き...

if __FILE__ == $0
  require 'gserver'

  class TestServer < GServer
    def serve(conn)
      GMRW::SSH2::Server::Service.new(conn).start
    end
  end

  server = TestServer.new(GMRW::SSH2::Server::DEFAULT_PORT)
  server.audit = true  # ログ機能を有効化
  server.start         # サーバ開始
  server.join          # サーバスレッドを待つ (スクリプトは動き続ける)
end

# vim:set ts=2 sw=2 et fenc=UTF-8:

ちょっとテスト

Service#start を下のように変えてテストしてみます。

  def start
    connection.puts "OK"   # 繋いできた相手に "OK" を返信する
  end

service.rb を起動します。(そのまま動き続けます。停止は CTRL-C で)

$ ruby gmrw/ssh2/server/service.rb
[Sun Jul 30 22:58:08 2011] TestServer 127.0.0.1:50022 start

TTY からポートを listen してるか念のため確認してみます。*4

$ n -p | g -w [r]uby
   :
tcp        0      0 localhost.localdo:50022 *:*     LISTEN   2402/ruby       

listen しています。

接続して確認してみます。telnet を使います。

$ telnet localhost 50022
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
OK
Connection closed by foreign host.

"OK" を返してきました。

サーバのログもでています。

$ ruby gmrw/ssh2/server/service.rb
[Sun Jul 30 22:58:08 2011] TestServer 127.0.0.1:50022 start
[Sun Jul 30 23:04:08 2011] TestServer 127.0.0.1:50022 client:59206 127.0.0.1<127.0.0.1> connect
[Sun Jul 30 23:04:08 2011] TestServer 127.0.0.1:50022 client:59206 disconnect

サーバを CTRL-C で一旦止めて、Service#start を以下のように変えてもう一度確認します。

  def start
    raise "NG"   # 例外を発生させる
  rescue => e
    connection.puts e
  end

サーバを起動して、telnet で接続します。

$ telnet localhost 50022
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
NG
Connection closed by foreign host.

"NG" を返してきました。


今回は、とりあえずここまで。

次はログですね。


*1:私の環境では OpenSSH の sshd を動かしています

*2UNIX では、ポート 0〜1023 の使用には root 権限が必要です

*3:49152〜65535 の範囲が DYNAMIC AND/OR PRIVATE PORTS です。(http://www.iana.org/assignments/port-numbers)

*4:n は 'netstat -tua' のエイリアス、g は 'grep' のエイリアスです