コンピュータネットワーク基礎
2003.5.30

Back to index page

  1. 本日の作業内容



  2. 電子メール配送 (SMTPとPOP3)

    電子メールの送受信をRubyスクリプトにより直接コマンド操作で実習します.こ れにより,実際にメールソフトが行っている動作を自分で確認します.

    • メールサーバ

      メールサーバがメールの送受信を行う際,やっていること自体は非常に単純なも のです.サーバ宛に送られてきたメールの差出人と宛先を調べ,そのメールを外 部に送り出すのか,自分のサーバ内のユーザに届けるのか,その判断の繰り返し です.しかし,実際に行われている作業はかなり複雑になってきています.それ は,メールの差出人が配達すべき適正なアドレスであるか,とか,宛先に示され ているアドレスへはどう送るのか,その判断が非常にややこしいからです.

      電子メールを利用した広告が近年非常に多くなっています.メールの宛先がたと えば何万人という大規模なものでも,クライアントからサーバに配送を依頼する 段階では単なる一通のメールです.サーバまで届くと,サーバがアドレスを分析 して,指定してあるアドレスに一通ずつ送ります.すなわち,無差別に近い広告 メールや迷惑メールも悪意を持って出す人間にとってはただの一通のメールであ り,サーバがその負担を負うという仕組みになっているため,サーバは配送を認 めるべきアドレスかどうかをきちんと見極める必要があるわけです.商用プロバ イダの多くは,そのために,自分の管理するメールアカウントでも,自分のドメ イン内からしか配送を認めないという設定を多く利用しています.メールサーバ を外部から不正に利用されるのを防ぐ目的です.

      また,配送先を調べるのもそれなりに手順が必要です.この授業の宿題提出用の アドレスである aegis@mag.shimane-u.ac.jp ですが, mag.shimane-u.ac.jp というサーバは大学内に存在しません. ネットワークの経路制御 (これを「ルーティング」といいます)は,IPア ドレスという数字記号で判断しますが,サーバの名前とアドレスの対応 を調べてどこに送信するかを判断するのですが,メールのドメインは必 ずしもサーバそのものではありません.次のコマンド nslookup を用いて調べてみましょう.

      
      $ nslookup www.ecs.shimane-u.ac.jp
      Server:  ifse1.riko.shimane-u.ac.jp
      Address:  10.70.240.140
      
      Name:    ecs.riko.shimane-u.ac.jp
      Address:  10.183.50.6
      Aliases:  www.ecs.shimane-u.ac.jp
      
      

      ここでは,このテキストが置いてある www.ecs.shimane-u.ac.jp とい うwebサーバのIPアドレスを調べています.すると, www.ecs.shimane-u.ac.jp というのは ecs.riko.shimane-u.ac.jp というサーバの「エイリアス」であることが報 告されています.このように,ネットワーク上にはサー バの名前そのものではないものが流通しているのです. 先ほどの,mag.shimane-u.ac.jp を同様に 調べると,

      
      Server:  ifse1.riko.shimane-u.ac.jp
      Address:  10.70.240.140
      
      Name:    mag.shimane-u.ac.jp.
      

      となって,サーバのIPアドレスが判明しませんでした.メールサーバのアドレス そのままでは長くて覚えにくかったりするので,このようなアドレスを簡略化す ることが行われています.そのような情報の記録をMXレコードと呼び,これも, ネームサーバが管理するのですが,いずれにしても,アドレスが適正であり送信 先として認められるかどうかをメールサーバはひたすらチェックし続けるのです. 送信先が実在しないものであれば,基本的に,差出人にその旨を告げるメールを 送信します.そのような作業もメールサーバの仕事です.

    • メールの送信方法

      電子メールの送信にはSMTP (Simple Mail Transfer Protocol) が利用されます. このプロトコルはユーザの認証を必要としないため,差出人が適正かどうかを調 べるように通常は設定されています.すなわち,自分の組織内のホストから出さ れた自ドメインのメールならば送信を認める,と言うようになっています.HTTP などと同じように,クライアントから特定のコマンドで要求を行い,サーバが返 答を返す仕組みを使っています.クライアントから要求できるコマンドの一例を 以下に示しておきます.

      HELOドメイン名を通知
      MAIL FROM:差出人を指定
      RCPT TO:宛先の指定
      DATAメール本文の送信
      QUITセッション終了

      また,クライアントからのコマンド要求に対してサーバが返答しますが,それは, 以下のようなコード (レスポンスコード) で行われます.

      220準備完了
      221セッション終了
      250アクション完了
      354メール本文の送信待ち
      400番台システムのエラー
      500番台コマンドのエラー

      これらのコマンドはSylpheedでメールを送信した後,「ツール」メニューの「ロ グウィンドウ」を開くと見ることが出来ます.送信がうまく行っていないときに は,この「ログウィンドウ」を開いて,どこに問題があるのかを確認するように してください.以下に示すのは,私の環境からテストメールを送信したときのロ グの例です.

      
      SMTPサーバ: mag2.riko.shimane-u.ac.jp に接続中...
      SMTP< 220 mag2.riko.shimane-u.ac.jp ESMTP Sendmail 8.9.3/3.7W; Mon, 27 May 2002 00:47:14 +0900
      SMTP> HELO plat
      SMTP< 250 mag2.riko.shimane-u.ac.jp Hello susc3012.riko.shimane-u.ac.jp [10.183.50.8], pleased to meet you
      SMTP> MAIL FROM: 
      SMTP< 250 ... Sender ok
      SMTP> RCPT TO: 
      SMTP< 250 ... Recipient ok
      SMTP> DATA
      SMTP< 354 Enter mail, end with "." on a line by itself
      SMTP> . (EOM)
      SMTP< 250 AAA12133 Message accepted for delivery
      SMTP> QUIT
      SMTP< 221 mag2.riko.shimane-u.ac.jp closing connection
      

      上述のコマンドが使用されているのが分かると思います.また,コマンドの一つ 一つについてサーバ側が確認して「OK」を出しているのも分かるでしょう.DATA コマンドで送り始めたメール本文は「.」によりそのファイルが終りであること を示します.最後はQUITで終わります.

    • メール送信プログラム

      Sylpheedを使うとメールの送信など簡単に行えて,自分では何が行われているの か気にすることもないでしょう.ここでは,Rubyのスクリプトを用いて,実際に サーバとやり取りするプログラムを組んでみましょう.ここにあげてある例は, 原信一郎著,「Rubyプログラミング入門」(オーム社,2000)の p.299に紹介され ているものです.スクリプトは長いので自分で入力するとたぶん間違いが多くな ると思いますので,コピーしてください.

      ダウンロード用ファイルも用意しました.右クリックで保存してください.

      
      #!/usr/bin/ruby
      
      require "socket"
      require "kconv"
      require "timeout"
      
      class Client
        def initialize(host, port)
          @host, @port = host, port
        end
        def open
          timeout(60) do
            @sock = TCPSocket.open(@host, @port)
          end
        end
        def close
          @sock.close
        end
        def puts(x)
          $stdout.puts x if $DEBUG
          @sock.print x, "\r\n"
        end
        def wait(regex, sec = 60)
          timeout(sec) do
            line = @sock.gets
            $stdout.puts line if $DEBUG
            unless regex =~ line
      	raise "Response line not matches #{regex.inspect}"
            end
          end
        end
      end
      
      class SmtpMail < Client
        attr_accessor :domain, :from, :to, :subject, :contents
        def initialize(host, from, to, subject, contents)
          @from, @to, @subject, @contents = from, to, subject, contents
          (myname, @domain), = @from.scan(/(.+)@(.+)/)
          super(host, 25)
        end
        def open
          super
          wait(/^220\s/)
          puts("HELO #@domain"); wait(/^250\s/)
        end
        def send
          puts("MAIL FROM:#@from"); wait(/^250\s/)
          puts("RCPT TO:#@to");     wait(/^250\s/)
          puts("DATA");             wait(/^354\s/)
          puts("Subject: #@subject") if @subject
          puts("")
          @contents.each do |line|
            puts Kconv.tojis(line.sub(/^\./, '..')).sub(/\r?\n?\z/, "")
          end
          puts("."); wait(/^250\s/)
        end
        def quit
          puts("QUIT"); wait(/^221\s/)
        end
      end
      
      host, to, from, subject = ARGV
      unless subject
        puts "Example: mail.rb server her@her.domain me@my.domain SUBJECT < mail.txt"
        exit
      end
      
      contents = $stdin.readlines
      mail = SmtpMail.new(host, to, from, subject, contents)
      mail.open
      mail.send
      mail.quit
      mail.close
      

      スクリプトの途中に先ほどのコマンドが随時登場してくるのが分かるでしょう. 処理としては,メソッドを定義している部分が多くて時系列で並んでいないので 分かりにくいかも知れませんが,HTTPクライアントと同様にソケットを開いてコ マンドを送っているだけです.ただし,サーバから送られてくるレスポンスコー ドを待って処理を進めるために,wait で返答を待ち,それが正規表現 で指定された正しいコードであるかを判断しています.

      このスクリプトの利用は,コマンドライン引数として,サーバ名,差出人,宛先, 件名を取り,リダイレクトとして本文を書いたファイルの名前を使用します.た とえば,以下のようにして,自分もしくは知り合いにメールを送信してください.

      $ ruby mail.rb matsu.ipc.shimane-u.ac.jp s0140**@matsu.shimane-u.ac.jp s0140**@matsu.shimane-u.ac.jp Script-test < mail.rb

      このスクリプト自体をメールの本文としていますので,スクリプトのあるディレ クトリでコマンドを実行するか,適切なパスを記述す るかしてください.また,上のコマンドは実際には一 行で入力してください.

    • メールの受信

      メールの受信にはPOP3 (Post Office Protocol version3) が利用されます.メー ルの読み出しにはユーザの認証が必要になるので,ユー ザとの対話的なやり取りが行われます.使用されるコ マンドの代表的なものを以下に示します.

      STATダウンロード可能メールの一覧を得る
      LISTメールの一覧もしくは引数で指定したメールの情報を得る
      RETR引数で指定した番号のメールを読む
      DELE引数で指定した番号のメールに削除マークをつける
      RSET削除マークを取り消す
      QUIT削除マークのついたメールを削除して終了する

      これについてもSylpheedの「ログウィンドウ」で実際の動作は確認できます.以 下に例を示します.

      
      POP3サーバ: mag2.riko.shimane-u.ac.jp に接続中...
      POP3< +OK QPOP (version 2.53) at mag2.riko.shimane-u.ac.jp starting.  <12102.1022425896@mag2.riko.shimane-u.ac.jp>
      POP3> USER nawate
      POP3< +OK Password required for nawate.
      POP3> PASS ********
      POP3< +OK nawate has 1 message (2498 octets).
      POP3> STAT
      POP3< +OK 1 2498
      POP3> LIST
      POP3< +OK 1 messages (2498 octets)
      POP3> RETR 1
      POP3< +OK 2498 octets
      POP3> DELE 1
      POP3< +OK Message 1 has been deleted.
      POP3> QUIT
      POP3< +OK Pop server at mag2.riko.shimane-u.ac.jp signing off.
      

    • メール受信スクリプト

      以下のスクリプトにより上記の動作を自分で対話的に行ってみましょう.スクリ プトは先ほどと同様に書籍のp.296に出ているものです.

      ダウンロード用ファイルも用意しました.右クリックで保存してください.

      
      #!/usr/bin/ruby
      
      require "socket"
      
      server, port = ARGV
      sock = TCPSocket.open(server, port)
      
      begin
        t = Thread.start do
          while b = sock.gets
            print b
          end
          exit
        end
        while line = $stdin.gets
          line.chop!
          break if line == ".."
          sock.print line, "\r\n"
        end
        t.exit
      ensure
        sock.close
      end
      

      このスクリプトの実行は,ターミナルでまずサーバへの接続から始めます.

      $ ruby pop.rb matsu.ipc.shimane-u.ac.jp 110

      最後の110というのはPOP3が使うポート番号です.以下にサーバからの応答と自 分で入力すべき情報について色分けして紹介しておきます.

      
      +OK QPOP (version 3.1.2) at lxfs.cosmos.shimane-u.ac.jp starting.  
      
      
      USER s0140**
      
      
      +OK Password required for s0140**.
      
      
      PASS hogehogehoge
      
      
      +OK s0140** has 1 visible message (0 hidden) in 1378 octets.
      
      
      LIST
      
      
      +OK 1 visible messages (1378 octets)
      1 1378
      .
      
      
      RETR 1
      
      
      +OK 1378 octets
      >From bin  Mon May 27 00:17:50 2002
       .....
      
      
      QUIT
      
      
      +OK Pop server at lxfs.cosmos.shimane-u.ac.jp signing off.
      

      全体を眺めてみると,Sylpheedの「ログウィンドウ」と同じような内容になっているのが分かるでしょ う.なお,ここでは入力したパスワードが見えてしまうので,それなりに注意し てください.また,LISTコマンドで実際に得られるメールの数はそれぞれ異なり ますので上と同じにならない場合もあります.DELEコマンドは使用しないように しましょう.Sylpheedであとから読めなくなります.

  3. 小テスト

    授業の終了20分前くらいにAクラスBクラス別の小テストを実施します.アナウ ンスに注意してください.また,提出は電子メールにより行いますので,メール の送信準備は忘れないでいて下さい.

  4. 宿題

    授業の終りに宿題(AクラスBクラス)の説明をしますので,アナウンスに注意してください.


目次ページへ戻る