FTP(File Transfer Protocol)〜前編インターネット・プロトコル詳説(10)

» 2001年07月17日 00時00分 公開
[梁瀬介次@IT ]

FTP〜原点はRFCを参照したいというニーズから

 FTPは、今日インターネット上で最もよく利用されるファイル転送用プロトコルとして知られる。ファイル転送用プロトコルとしては、Windowsネットワークなどで利用されるNetBIOSやUNIXでは一般的なNFS(Network File System)などもあるが、これらはいわゆる「ファイルマウント型」(OSのファイルシステムに対して外部ファイルシステムを仮想的にマウントする)であるのに対し、FTPは純粋なクライアント/サーバ型である。操作上は前者が便利なのにも関わらず、FTPがいまだにインターネットで標準プロトコルとされているは、ある種のOSに依存しない「マルチプラットフォーム」での動作を当初から考慮していたからだ。

1970年ごろ このころまでには、すでに異なるホスト間でファイルを転送するという最もシンプルなFTPの前身ともいうべき機能が、UCLAなどを中心に実現されていたようだ
1973年 さまざまな議論を経て、最初のFTP仕様(RFC454)が発表される。コントロールとデータコネクションの分離、転送モードなどの基本的なアイデアは、すでにこの時点でまとまっていた
1985年 RFC765に代表されるやはり多くの議論を経て、現在のFTP仕様となるRFC959(STD9)が発表される。従来からの最も大きな変更点は、1982年に確定されたTCP/IPへの対応であった
表1 FTPの歴史年表

 プロトコル自体の歴史も非常に長く、30年以上前にインターネットの前身であるARPAネットで開発された。その後、仕様は大きく変わっていない。RFCはインターネットでの標準を規定するドキュメントなのはご存知のとおりだが、元々FTPはこのRFCをオンラインで参照したり交換したいというニーズから生まれたというから、その歴史が実感できるのではないだろうか。

RFC959
(STD9)
"FILE TRANSFER ROTOCOL (FTP)"
RFC1123 "Requirements for Internet Hosts -- Application and Support"4章にRFC959の再定義がある
RFC1579 "Firewall-Friendly FTP"
RFC2228 "FTP Security Extensions"
RFC2640 "Internationalization of the File Transfer Protocol"
表2 FTP関連の標準仕様

 また、これまで解説したほかのインターネット・プロトコルに比べて、いくぶん複雑で、多少毛色も異なるようにも感じられるだろう。ほかのプロトコルは、周りに存在するそれ以外のプロトコルと互いに影響を与えつつ生まれてきたのに対して、FTPはその歴史ゆえ、独自の特徴を持つことになったのも事実だ。とはいえ原理は単純であり、そのシンプルさゆえにここまで広く受け入れられてきた点では、ほかのインターネット・プロトコルと何ら変わりはない。

FTPの2重構造

 今回の説明に入る前に一点注意がある。これまで連載を読まれてきて「FTPのプロトコル」と聞くと、キャラクタベースのFTPコマンドを思い出した方がいるのではないだろうか。実はFTPのプロトコルは、それらのコマンドとはまったくの別物だ。コマンドはあくまでFTPのクライアントプログラムが提供しているもので、FTPのプロトコルではないのだ。

 これは一体どういう意味なのだろうか。下図を見てほしい。

図1 FTPの概念図。RFC959(STD9)からの抜粋 図1 FTPの概念図。RFC959(STD9)からの抜粋

 これは簡単なFTPの動作概念図だ。すでに述べたように、FTPはクライアント/サーバモデルを採っている。そしてクライアントとサーバ双方に「PI」と呼ばれるモジュールが存在している。PIは「プロトコル・インタプリタ」という意味で、クライアントからの命令やサーバへの指示を、実際のプロトコルへと「翻訳」するためのものだ。

 例えば、皆さんは普段どんなツールからFTPを利用しているだろうか。CUI(Character User Interface)のFTPコマンドか、それともGUIのFTPツールか、あるいはWebブラウザでの利用だろうか。CUIのFTPコマンドであれば、「dir(ディレクトリの表示)」や「mget(複数ファイルの取得)」などを入力して利用することになる。クライアント側PIはこれらユーザーからのFTPコマンドを解釈して、それをFTPのプロトコルへ変換し、サーバへと命令するのだ。またサーバ側PIでは、プロトコルの内容をサーバ側での動作に変換して、指定された動作を実行する。つまり、先ほどのmgetコマンドは、サーバまで直接届いているわけではない。PIによって解釈され、実際には別のコマンド(つまりFTPのプロトコル)が裏で実行されているのだ。後述するが、mgetのように「複数のファイルを転送する」というプロトコルはFTPには存在していない。内部では、これを複数の「単一ファイルの転送」処理に分割して実行しているのだ。

 HTTPやSMTPに比べると(プロトコル自体にはそれほどのインパクトはないものの)、何とも面倒な実装をクライアントやサーバのプログラムで行わないといけないわけだ。しかし、これには3つの利点がある。1つは、PI(特にクライアント側)によって、CUIのコマンドやGUIツールといった異なるインターフェイスを持つFTPクライアントが、同じプロトコル実装部分を共有できることだ。いうなれば、プレゼンテーション層とロジック層の分離である。さまざまなAPIが流通している現在ではメリットは薄いが、特にFTPが開発された初期には、こうしたロジックの共通化はFTPの一般化に有効であった。

 次に、PIからDTP(Data Transfer Proccess:使用されるファイルシステムのアクセス部分)を切り離し、個々のOSごとの実装が可能となる。これも現在でこそ問題になることは少ないが、一口にファイルの転送といっても、実はOSごとにファイルシステムの仕組みや実装は異なる。単なるテキストファイルであっても、格納構造やフォーマットはOSごとにそれぞれ異なっているのが普通だ。単に転送しただけでは、相手が別のOSだったとして、そのまま利用できる保証はない。こうしたファイルアクセス部分をモジュール化し、転送時にはその差異をDTPで吸収して変換することで、多様なOSに対応できるマルチプラットフォーム化を目指すという意図なのだ。

 3番目に、プロトコル自体を明らかに単純にできる。FTPクライアントでは単純なプロトコルを組み合わせることで、より複雑な動作を実行できるからだ。プロトコルの簡易さは組み合わせによるクライアントプログラムの多様化を実現でき、プロトコルの浸透にも役立つことになる。

コントロールとデータ

 もう一点、FTPの大きな特徴が、「コントロールコネクション」と「データコネクション」の分離だ。もう一度、先の図1を見てほしい。クライアント側のユーザーPIは、サーバ側のサーバPIとコネクションを作成する。通常、サーバではポート21番を使用し、ユーザーからのアクセスを待ち受けている。これを「コントロールコネクション」と呼び、HTTPやSMTPと同じようにクライアントからのコマンドを受け入れ、結果ステータスなどを返答する。

 しかし、実際のファイルデータの転送やファイル一覧データなどは、DTPが担当する「データコネクション」を通じて行われる。HTTPやSMTPでは常に1つでしかない接続が、FTPでは2つ利用されるのだ。

図2 コントロールとデータの使い分け 図2 コントロールとデータの使い分け

 データコネクションが別に存在しているのは、コントロールコネクションを通じて、データの転送などの制御をスムーズに行うためだ。データの転送をデータコネクションで行うようにすると、途中でキャンセルなどの介入を行いたい場合に、コントロールコネクションを通じてサーバへ命令することができる。もしコネクションが1つであれば、大容量ファイルの転送中などにその処理を終了したくなっても、サーバへ指示する術がなく、転送終了までユーザーは待ち続けなければならないだろう。

 なお、データコネクションで送受信されるのは、あくまで送受信するファイル内容とファイル一覧の結果だけだ。結果ステータスなどは、コントロールコネクションを通じて返答されるので注意しよう。

 いくぶん複雑ではあるが、非常に洗練されたメカニズムを持つ、インターネット・プロトコル“らしからぬ”プロトコル、それがFTPなのだ。

基本フローとコマンド

 プロトコルの構造や思想が、ほかのインターネット・プロトコルとは異なっている点は理解していただけたかと思う。しかし、実際のプロトコル内容にそれほどの違いはない。FTPもやはりTelnetをベースにしており、ASCIIコードをベースとしたコマンドとレスポンスによって実現されている点では同じだ。

 FTPにおいても、リクエストはコマンドとオプションからなる単一行のテキスト(ASCII)だ。レスポンスは次のようなフォーマットをとる。

レスポンスコード) (メッセージやステータス

 レスポンスコードは3桁の応答結果を示す番号だ。個別に番号に対応する意味が定められている。レスポンスが複数行に渡る場合には、レスポンスコードに続く空白が「-(ハイフン)」となり、次に続く行があることを示す。また、レスポンスコードに空白が続く行の登場により、最終行であることを示している。

 レスポンスコードは次のカテゴリーに従って決められている。また、レスポンスコードの詳細は別表1にもまとめた(こちらをクリックすると、別ウィンドウでレスポンスコードの一覧を表示しますをクリックすると、別ウィンドウでレスポンスコードの一覧を表示します)。

1xx 肯定的な予備応答 正常な動作だが、引き続き何らかの別の動作を待つ必要がある
2xx 肯定的な完了応答 通常の完了を示す
3xx 肯定的な中間応答 正常な動作だが、引き続き追加のコマンドを必要としている
4xx 一時的な否定応答 一時的な理由で完了されなかった
5xx 否定応答 何らかの理由で完了されなかった

x0x 文法エラー
x1x インフォメーション
x2x 接続関連
x3x 認証関連
x4x 未使用
x5x ファイルシステム関連
表3 レスポンスコードのカテゴリー

 では、基本的なプロトコルフローの例とその動作を見てみよう。

図3 プロトコルフローの例 図3 プロトコルフローの例

認証と処理の終了

 クライアントはまず、サーバのコントロールコネクションに接続しなければならない。その際に認証に用いるのがUSERコマンドとPASSコマンドだ。ここでは、ユーザー名とパスワードを指定する。一般には、このときに指定したユーザー権限でサーバのファイルへとアクセスできる。QUITコマンドはコントロールコネクションと、その際に存在していればデータコネクションをクローズする。

USER  ユーザー名

PASS  パスワード

QUIT


データコネクションの作成と利用

 PORTコマンドは、サーバにデータコネクションのためのIPアドレスとポート番号を通知するためのコマンドだ。データコネクションは、前述のように、ファイルデータの送信と受信、ファイル一覧情報の取得などを行うための専用コネクションである。

PORT  IPアドレスとポート番号


 コントロールコネクションは、まずクライアントからサーバへ向けて作成される。これは一般的なクライアント/サーバモデルに基づいている。次にクライアントはファイルの送受信などに先駆けて、PORTコマンドを実行する必要がある。PORTコマンドは次のようなフォーマットの引数をとる。

(例) 192, 168, 1, 10, 4, 25

 第1引数から第4引数まではIPアドレスを示す。4バイトのIPアドレスの各バイトを10進数で表記したものだ。通常は、クライアントのIPアドレスを指定する。上記の例では、「192.168.1.10」という意味になる。第5引数と第6引数は、2バイトのポート番号の各バイトを10進数で表記したものだ。上記であれば、「4×256+25=1049」を示している。

 データコネクションにおいては、先のコントロールコネクションとは逆に、サーバからクライアントに向けてコネクションを作成する仕様になっている*1。つまり、一般のクライアント/サーバとは逆の方向でデータコネクションは張られるのだ。クライアントはあらかじめIPアドレスと使用するポートをサーバに知らせ、そのポートでデータコネクションの受け入れ用ソケットをオープンしておく。サーバはそのポートに対してデータコネクションを作成し、データの送受信を行う、というわけだ。

 なお、データコネクションはファイルの送受信のたびに作成する必要がある。つまり、ファイルの送受信などに先立って毎回PORTコマンドが必要になるのだ。また、データコネクションでのサーバ側ポート(接続元ポート)は、通常、20番が用いられることになっている。

*1厳密には、この説明はあまり正確ではないかもしれない。より正しくは、FTPクライアントプログラムは「データコネクション向けサーバとしても動作する」ということになる。逆にFTPサーバプログラムは、データコネクションのためのクライアント機能も持ち、データコネクションに関してはクライアントとして動作する。それぞれがクライアント/サーバの両方の機能を有しているのだ。HTTPやPOPクライアントに比べると、複雑な仕様であることが分かってもらえるだろう


データタイプの指定

 データタイプというのは、「データの変換モード」の選択だと思えばよい。この機能はよく知られているだろう。一般には、アスキー(ASCII)かバイナリ(イメージとも呼ばれる)かを選択することになる。

TYPE データタイプ


 アスキーモードの元々の意味は、テキストデータに含まれる改行コードなどOSに依存したコードを、OSに依存しない共通の体系へ変換して送信することを目的としている。こうした共通体系は「NVT-ASCII」と呼ばれ、実はTelnet仕様でのコード体系と等しい(つまりはASCIIだ)。送信時には、元々の体系からこうした共通体系へ一度変換し、受信時には再びそれぞれのOS依存型へと再変換することで、個々のOSに対応した送受信環境を実現している。例えば、UNIXとWindowsで改行コードが異なっても、アスキーモードで送受信することで改行コードがちゃんと変換されるのは、変換時にそれぞれのOSの改行コードへと相互変換しているからなのである。

 これに対してバイナリでは、文字どおり変換を行わずOSで格納されているデータをそのまま送受信するモードである。そのほか「EBCDIC」と呼ばれるモードも用意されている。これは、IBM系汎用機で使用される文字コードであるEBCDICで送受信するモードだが、現在ではほとんど使用されることはない。また、「ローカルモード」と呼ばれる内部表現を送信側が規定するモードも定義されているが、これもほとんど使用されることはないだろう。

ファイルの送受信

 あらかじめデータコネクションを作成しておけば、クライアントはサーバとの間でファイルの送受信が行えるようになる。

RETR ファイル名

STOR ファイル名


 ファイルの受信を行うのがRETRコマンドだ。実行すると、データコネクションを通じて、指定したサーバ上のファイルの内容が取得できる。データコネクションでは当然のことながら、そのファイルの内容のみが送られてくるだけなので、FTPクライアントではその内容をもとにローカルディスクにファイルを作成する必要がある。

 STORコマンドは逆に、データコネクションを通じてサーバにファイル内容を送信する。送信した内容は、引数で指定した名前のファイルとして保存される。すでに同じ名前のファイルが存在している場合には、上書き保存されることになる。

 FTPコマンド(CUI)でのgetputコマンドは、内部でこれらのコマンドを使用している。またすでに述べたように、FTPにmgetmputに該当する複数ファイルの送受信機能は存在していない。RETRやSTORを複数実行することで、機能を実現しているのだ。


 次回後編では、そのほかのコマンドやFTPの特別な機能について見てみよう。


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。