コンピュータネットワーク基礎
2003.5.30
電子メールの送受信を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: |
上述のコマンドが使用されているのが分かると思います.また,コマンドの一つ 一つについてサーバ側が確認して「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であとから読めなくなります.
授業の終了20分前くらいにAクラスと Bクラス別の小テストを実施します.アナウ ンスに注意してください.また,提出は電子メールにより行いますので,メール の送信準備は忘れないでいて下さい.
授業の終りに宿題(AクラスとBクラス)の説明をしますので,アナウンスに注意してください.