犬ターネット

Perl の IO::Socket::INET 経由の HTTPアクセスで 400 Bad Request が出る

2019-01-31 perl http apache

社内検証サーバがぶっ壊れたので新しくサーバ構築しつつ動作確認中。そんな中、IO::Socket::INET を使って HTTP アクセスを行う perl スクリプトを動かしたところ、リクエスト先から「400 Bad Request」が返ってきてしまい接続不可。perlスクリプトはこんな具合。

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;

my $sock = IO::Socket::INET->new(
  PeerAddr => '192.168.0.20',
  PeerPort => '80',
  Proto => 'tcp'
);

my $out = "";
my $lf = "\n";
my $request_header =
    "GET /test.php HTTP/1.0$lf"
  . "UserAgent: testagent$lf$lf";

print $sock $request_header;
$sock->flush();
my $ret_code = <$sock>;

while (<$sock>){ m/^\r\n$/ && last; }
while (<$sock>){ $out .= $_; }
$sock->close() if($sock);

print $ret_code . "\n";
print $out . "\n";

今まで普通に動いていたのになんでだ…。全然原因がわからないので、リクエスト先(192.168.0.20)の Apache の httpd.conf を以下のように修正して apache 再起動しデバッグモードに変更。

LoadModule dumpio_module modules/mod_dumpio.so
LogLevel debug
DumpIOInput On

これで perl スクリプトを実行したところ、/var/log/error_log に以下が出力された。

[Thu Jan 31 15:28:26 2019] [debug] mod_dumpio.c(113): mod_dumpio: dumpio_in [getline-blocking] 0 readbytes
[Thu Jan 31 15:28:26 2019] [debug] mod_dumpio.c(55): mod_dumpio:  dumpio_in (data-HEAP): 23 bytes
[Thu Jan 31 15:28:26 2019] [debug] mod_dumpio.c(74): mod_dumpio:  dumpio_in (data-HEAP): GET /test.php HTTP/1.0\n
[Thu Jan 31 15:28:26 2019] [debug] protocol.c(1260): [client 192.168.0.19] request failed: malformed request line

malformed request line でピンときた。多分改行コードのエラーだ。HTTPリクエストの改行コードは \n ではなく \r\n にしないといけない。先ほどの perl スクリプトの改行コードを修正。

# my $lf = "\n";
my $lf = "\r\n";

これで再度perlスクリプトを実行したところ正常に実行された。でもなんで今まで動いていた…? Apache に自動で改行コードを置換する設定ってあったっけか…。で調べていたら以下の記事がヒット。困ったときのスタックオーバーフローだわマジで。

mod rewrite - Have Apache Accept LF vs CRLF in Request Headers - Stack Overflow

最近の apache のバージョンではよりセキュアになり制限が増えたらしい。制限を緩めるためには過去のバージョンにダウングレードするか、以下設定を httpd.conf に記述して apache を再起動すればよい。

HttpProtocolOptions unsafe

手元の環境では perlスクリプトに手を入れることなく apache の設定変更のみで無事に動いた。ただし unsafe な設定なので、本来であれば perl プログラムの方を直すべき。

人力音楽レコメンドエンジン
餅、ボヘミアンラプソディ、カニ