コンピュータネットワーク基礎
2004.6.11
電子メールの送受信をRubyスクリプトにより直接コマンド操作で実習します.こ れにより,実際にメールソフトが行っている動作を自分で確認します.
この授業ではメールの送受信にSylpheedを利用していますが,教科書の 10.3(p.108)にあるように,UNIXでは初期にはOSの機能を利用してメールの送受 信を行うことが普通でした.そのため,今でもその環境は残っています.ここで, 試してみましょう.ターミナルで,
のようにコマンド入力してみましょう.アドレスは自分宛としてください.メー ル本文記入モードになります.適当な文字を入力して改行します.なお,このモードでは文字の修正などの編集はできません.間違え たら諦めましょう.文章入力が終わったら,最後に半角文字でピリオド だけを入力する行をおきます.
教科書の図10.6の中の「最後の行」の次の行のピリオドがこれに対応します.次にCc:を聞かれますが,ここではそのままEnterキーを押します.これで送信が 完了しますので,Sylpheedで受信をしてみましょう.また,ヘッダの確認も行い ましょう.
メールサーバがメールの送受信を行う際,やっていること自体は非常に単純なも のです.サーバ宛に送られてきたメールの差出人と宛先を調べ,そのメールを外 部に送り出すのか,自分のサーバ内のユーザに届けるのか,その判断の繰り返し です.しかし,実際に行われている作業はかなり複雑になってきています.それ は,メールの差出人が配達すべき適正なアドレスであるか,とか,宛先に示され ているアドレスへはどう送るのか,その判断が非常にややこしいからです.
電子メールを利用した広告が近年非常に多くなっています.メールの宛先がたと えば何万人という大規模なものでも,クライアントからサーバに配送を依頼する 段階では単なる一通のメールです.サーバまで届くと,サーバがアドレスを分析 して,指定してあるアドレスに一通ずつ送ります.すなわち,無差別に近い広告 メールや迷惑メールも悪意を持って出す人間にとってはただの一通のメールであ り,サーバがその負担を負うという仕組みになっているため,サーバは配送を認 めるべきアドレスかどうかをきちんと見極める必要があるわけです.商用プロバ イダの多くは,そのために,自分の管理するメールアカウントでも,自分のドメ イン内からしか配送を認めないという設定を多く利用しています.メールサーバ を外部から不正に利用されるのを防ぐ目的です.
また,配送先を調べるのもそれなりに手順が必要です.この授業の宿題提出用の アドレスである justice@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番台 | コマンドのエラー |
実際にはサーバとクライアントの間で対話的に処理が進められるので,教科書の 図10.6のようなやりとりが行われます.
これらのコマンドは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 で返答を待ち,それが正規表現 で指定された正しいコードであるかを判断しています.
このスクリプトの利用は,コマンドライン引数として,サーバ名,差出人,宛先, 件名を取り,リダイレクトとして本文を書いたファイルの名前を使用します.た とえば,以下のようにして,自分もしくは知り合いにメールを送信してください.
このスクリプト自体をメールの本文としていますので,スクリプトのあるディレ クトリでコマンドを実行するか,適切なパスを記述するかしてください.また, 上のコマンドは一行で入力してく ださい.
この授業では,テストや宿題の提出に「書類の添付」という形式を利用していま す. これには,ASCII文字しか送受信できない電子メールにバイナリーデータや日本語 文字列を取り込むための拡張形式,MIMEを利用しています.ここでは,バイナリー データのコード化について確認しましょう.
利用されているコンピュータの種類によっていくつかの形式が利用されてきました. 代表的なものとして,uuencode,binhex,base64があります.uuencodeとbinhexは,それぞ れUNIX,MacOSで主として利用されるもので今でも使用されていますが,相手の 利用環境が不明な場合などプラットフォームに依存しない形式としては最後の base64が適していると言えます.
この方式は,MIMEに適応するために開発されたものです.もともとのバイナリー データは8ビット符号化が行われていることが普通ですが,文字コードのときに 学習したように電子メールでは7ビット符号化が必要とされています.そこで,次のような方法によって コード化します.
バイナリーデータ列の情報を読み込む際に,3バイトずつに区切って取り込みま す.
3バイトのデータ(24ビット)を6ビットずつ4つのデータに分けます.
6ビットのデータを変換表に従い,文字に変換します.
十進 表記 | 変換 文字 |
十進 表記 | 変換 文字 |
十進 表記 | 変換 文字 |
十進 表記 | 変換 文字 |
---|---|---|---|---|---|---|---|
0 | A | 16 | Q | 32 | g | 48 | w |
1 | B | 17 | R | 33 | h | 49 | x |
2 | C | 18 | S | 34 | i | 50 | y |
3 | D | 19 | T | 35 | j | 51 | z |
4 | E | 20 | U | 36 | k | 52 | 0 |
5 | F | 21 | V | 37 | l | 53 | 1 |
6 | G | 22 | W | 38 | m | 54 | 2 |
7 | H | 23 | X | 39 | n | 55 | 3 |
8 | I | 24 | Y | 40 | o | 56 | 4 |
9 | J | 25 | Z | 41 | p | 57 | 5 |
10 | K | 26 | a | 42 | q | 58 | 6 |
11 | L | 27 | b | 43 | r | 59 | 7 |
12 | M | 28 | c | 44 | s | 60 | 8 |
13 | N | 29 | d | 45 | t | 61 | 9 |
14 | O | 30 | e | 46 | u | 62 | + |
15 | P | 31 | f | 47 | v | 63 | / |
実際に試してみましょう.例えば,abc という文字をbase64で変換し ます.それぞれの文字コードは hexdump コマンドで求めることができ るのは,以前に実習したので分かると思いますが,それぞれ16進表記で, 0x61, 0x62, 0x63 です.
ASCII文字だけであれば,http://www.psl.ne.jp/perl/pdojo00b.htmlのようなサイトが利用できることも以前に示しました.これを2進数表記にすると,
01100001 01100010 01100011 |
になります.この3バイトを6ビットずつの4つに区切り直すと,
011000 010110 001001 100011 |
になります.2進表記のままでは分かりにくいので10進表記に直すと,
24 22 9 35 |
ですから,上の変換表に従って文字を割り当てると,結局以下のようになります.
Y W J j |
原理から分かるように,base64を利用すると変換後のデータは変換前のデータの 4/3倍の容量になってしまいます.
授業の終了20分前くらいに Aクラスと Bクラス 別の小テストを実施します.アナウ ンスに注意してください.また,提出は電子メールにより行いますので,メール の送信準備は忘れないでいて下さい.
授業の終りに宿題( Aクラスと Bクラス )の説明をしますので,アナウンスに注意してください.