詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

LWPで"dh key too small"ってなったときの対処

ひさびさに Perl の話。結構前のことだけど、OS のバージョンを Debian 10 (Buster) にしたら LWP で一部のサイトにアクセスできなくなってしまった。

500 Can't connect to ***.com:443 (SSL connect attempt failed error:141A318A:SSL routines:tls_process_ske_dhe:dh key too small) at ...

こんな感じ。

OpenSSL のグローバルな設定を変更するとこのへんの挙動は変えられるとのことだけど、当然そんなことをしたいわけではないので、Perl (LWP) 側から設定したかった。結局このようになった。

my $ua = LWP::UserAgent->new(
    ssl_opts => {
        SSL_create_ctx_callback => sub {
            my $ctx = shift;
            Net::SSLeay::CTX_set_security_level($ctx, 1);
        },
    }
);

ネストが深いが、こういう流れ。

LWP::UserAgent->new に渡す ssl_opts の値が IO::Socket::SSL に渡されるので、ここでディープな設定を仕込むことができる。

SSL_create_ctx_callback というフックで、Net::SSLeay によって作成された ctx という値を受け取れる。この ctx は OpenSSL 由来のもので、これに対して Net::SSLeay の 低レイヤの API を使って OpenSSL を直接触ることができるようになる。ここでおもむろに SSL_CTX_get_security_level を呼ぶことで、冒頭の問題を解決することができるのです。

ところで SSLeay ってなんやねん! ってずっと思ってたけど OpenSSL の前身がこういう名前だったらしい。なるほどなー。いや eay ってなんなん?

参考にしたもの

Router::Simple::Reversible という Perl モジュールを書いた

https://metacpan.org/pod/Router::Simple::Reversible

だいぶ昔に書いていたのを shipit しました。tokuhirom さんの Router::Simple に、いわゆるリバースルーティングを足しただけのモジュール。Router::Simple を継承しているので、bless しなおすなりして使えばよいかと思います。

新しく生えるメソッド $router->path_for$router->match で返ってくるような hashref を渡すと、いい感じにルーティングのルールが具体化されたかたちで文字列が返ってくるシンプルな API です。テスト を見るとだいたい使い方がわかります:

my $router = Router::Simple::Reversible->new;

# from Router::Simple's pod
$router->connect('/', {controller => 'Root', action => 'show'});
$router->connect('/blog/{year}/{month}', {controller => 'Blog', action => 'monthly'});
$router->connect('/wiki/:page', { controller => 'WikiPage', action => 'show' } );
$router->connect('/download/*.*', { controller => 'Download', action => 'file' } );

is $router->path_for({ controller => 'Root', action => 'show' }),
   '/';

is $router->path_for({ controller => 'Blog', action => 'monthly' }, { year => 2015, month => 10 }),
   '/blog/2015/10';

is $router->path_for({ controller => 'WikiPage', action => 'show' }, { page => 'HelloWorld' }),
   '/wiki/HelloWorld';

is $router->path_for({ controller => 'Download', action => 'file' }, { splat => [ 'path/to/file', 'xml' ] }),
   '/download/path/to/file.xml';

is $router->path_for({ controller => 'NoSuchController', action => 'show' }),
   undef;

何か変なところがあったら GitHub リポジトリ のほうまでお知らせください。

Shipped ARGV::JSON 0.01

English entry is here.

ARGV::JSON というモジュールをリリースいたしました。Perl では <> という特殊な演算子で、プログラム引数として与えられたファイルやら標準入力やらをよしなに読み込むことができます(デフォルトだと行ごと)が、この ARGV::JSON を use しておくと、<> から 1 行読み込む代わりに、パースされた JSON データが返ってくるようになります。

実例を見ると明らかでしょう:

% curl -s https://api.github.com/users/motemen | perl -MARGV::JSON -E 'say <>->{blog}'
http://motemen.github.io/

jq のようなことが Perl でもできますね!

perlワンライナーで便利な -n-p といったオプションも <> を利用しているので、$_ を使った形のコードにすることもできます:

% curl -s https://api.github.com/users/motemen | perl -MARGV::JSON -nE 'say $_->{blog}'
http://motemen.github.io/

ちなみに複数の JSON が与えられた場合はそれを順番に読んでいくようになってます。どうぞご利用ください。

はてなで一緒に働きませんか?