私が最初に情報系のバイトをしたときには、 telnet についてはこう教えられた。「サーバにログインしてそのサーバを操作できるようにするコマンドだよ。でもパスワードとかを平文で送ってしまったりするからセキュリティ的に危ないんだ。だから絶対sshを使いなさい」これは間違いではない。 Wikipedia にもそのように書いてある。Telnet
しかし telnet には apache にアクセスして HTTP メソッドをやり取りする機能もある。と言うと、多分的を外すのだろう。何らかの理由で apache が telnet プロトコルに対応している、と言う方が正しい気がする。ここら辺は実際どうなのか?軽く apache telnet プロトコル あたりでググってみたが、その辺りを詳しく説明しているページが見つからなかった。自分の Mac で apache を動かし、 telnet でアクセスできることを確認したが、 ps aux | grep telnetd や ps aux | grep inetd を試してみても、どちらのデーモンも動いていなかった。このことからも、 telnet サーバが常に動いていてポート番号次第で apache へのアクセスを捌いてくれるなどということはあり得ないことが分かる。だから多分、 apache が何らかの理由で telnet クライアントからの操作に対応しているのだろう。Telnetにテキストベースのソケット通信のクライアントとしても利用することが可能であると書いてあった。別に apache に限った話ではないのか。
今日のバイトではこの telnet を用いてある HTTP クライアントの振りをしてあれこれやった。今思い返してみると簡単なことだったが、バイト中はそれが分かんなかったし自分で調べられなかったので、ここで備忘録として、メモ。
telnet を用いて www.example.com の apache にアクセスするには
telnet www.example.com 80
最後の 80 は apache のポート番号なので、場合によっては変更する。ちなみにこのポート番号を指定し忘れると、 telnet サーバのデフォルトのポート番号である 23 番が使われる。これは apache が使っているものではないので、仮にアクセスできたとしても HTTP 通信は出来ない。
telnet で apache にアクセスした後、ファイルを GET するには
GET /ファイルパス HTTP/1.1 Host: www.example.com
リクエストの最後は二回 return (enter) を送る。HTTP/1.1 を使ったときは、 Host: www.example.com を必ず付けねばならない。その辺りの話は http://q.hatena.ne.jp/1153884328 でされている。また、 HTTP/1.0 を使った場合であっても、接続したサーバがバーチャルホストを使っている場合は Host: ヘッダが必要になる。http://ja.wikipedia.org/wiki/HTTP より。
その他のヘッダに関しては、 http://www.studyinghttp.net/header が詳しい。
telnet で apache にアクセスした後、1byte文字テキストを POST するには
POST /POSTしたいパス HTTP/1.1 Host: www.example.com Content-Length: 1byte文字テキストの長さ 送りたい1byte文字テキスト
Content-Length: までは HTTP ヘッダ、一行改行した後に送る部分を メッセージボディと呼ぶらしい。
これに関しては、非常に話が複雑であるらしい。上記のように単に1byte文字のテキストを送りたいだけならば、 HTTP ヘッダに Content-Length: を付けて一行改行し、メッセージボディとして送りたい1byte文字テキストを Content-Length で指定した通りの長さで書けばいい。当然改行コードも文字数に数えられる。ちなみに私が使っている Apache/2.2.9 では POST メソッドを使っていても、 Content-Length を指定せずに メッセージボディを書こうとして一行改行したらそれで HTTP ヘッダが終わりと解釈されてしまったらしく、メッセージボディを書く余地無くサーバからレスポンスが帰ってきてしまった。
では POST メソッドを使うときは Content-Length が必須なのか?と言うと、そうでもないらしい。 http://www.studyinghttp.net/header#Content-Length を読むと、転送エンコーディングというものがなされているときは Content-Length は書かれてはならないというのだ。また、便宜上 Content-Length で指定するのは1byte文字テキストの長さと書いたが、正確には『10進数のオクテット』であるらしい。オクテットについては Wikipedia の オクテット を参考のこと。つまるところ、送りたいデータサイズを1byte単位の10進数で書け、ということらしい。
で、肝心の転送エンコーディングとは何なのか? http://www.studyinghttp.net/body#Transfer_Codings を読んでみたものの、正直よく分からなかった。
分からないことはすっきりしないが、ここは telnet を用いて apache にアクセスしたときの話なので、転送エンコーディングについては深く考えないことにする。
telnet で apache にアクセスした後、Basic認証を通してファイルにアクセスするには
GET /ファイルパス HTTP/1.1 Host: www.example.com Authorization: Basic base64エンコーディングされたユーザ名とパスワード
で大丈夫なはず。あいにく自分では実験してないが、 http://x68000.q-e-d.net/~68user/net/http-auth-1.html を参照のこと。base64エンコーディングされたユーザ名とパスワードを作る方法も、上記のサイトに書いてある。
というわけで、今日の telnet に関する復習は終わり。
余談であり本題であるが、分からないところに突き当たったときの解決の仕方、というのはどうすればいいのだろう?今回私が詰まっていたのは、 HTTP ヘッダとメッセージボディの間には一行改行を入れなければならないということが分からなかった点なのだが、この間違いに対するエラーメッセージは以下のようなものだった。
HTTP/1.1 400 Bad Request Date: Wed, 19 Nov 2008 07:50:42 GMT Server: Apache/2.2.10 (Unix) mod_ssl/2.2.10 OpenSSL/0.9.8i mod_perl/2.0.4 Perl/v5.8.7 Content-Length: 875 Connection: close Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>400 Bad Request</title> </head><body> <h1>Bad Request</h1> <p>Your browser sent a request that this server could not understand.<br /> Request header field is missing ':' separator.<br /> (後略)
この Request header field is missing ':' separator. という一文、今思えばメッセージボディとして付けたつもりの行の直前に改行が入っていなかったため、その行を apache は HTTP ヘッダの一部と解釈して、 HTTP ヘッダ名とパラメータの間にあるはずのコロンがないよ、と言っていたのだが、この一文でグーグル検索してみても上手いこと解説してくれているページが見つからなかった。と思っていたが、今検索してみたらあった。http://d.hatena.ne.jp/fact-real_09_internship/20080709 の telnet の操作・2と3。
こういうのをちゃんと調べ尽くさないうちに人に頼ってしまう姿勢がいけないんだよな。明らかにチームの足を引っ張ってる。なんて言うか、もっと情報収集能力が高い人になりたい。