2010-06-21

web で公開しているファイル群を BitTorrent で配布するには:その 2

web で公開しているファイル群を BitTorrent で配布するにはのつづき.

前の記事で書いたように BitTorrent の metainfo ファイルを作り,Vuze でのダウンロードに成功したように見えたのだが,問題が見つかった.400 点弱のファイル群を 1 つの metainfo で配布しようとしたのだが,何度試してもある piece でダウンロードが停止してしまい,以降の piece はどれもダウンロードされなかった.Vuze のログを見たところ「IP Filters」でブロックされてしまっていた.デフォルト設定では「Block peers that consistently send bad data」するようになっており,これによって web server が peer としてブロックされていたようだ.「Tools」---「IP Filters」メニューで示される「Blocked IPs」ウィンドウ中の「IPs that have sent bad data - banned if limits exceeded」に web server が加えられていた.

なぜ不正なデータを送っていると見なされたのだろうか.Vuze Forums: Problems with http seeded torrents ... によると,SHA1 ハッシュ値が合致しないとそう扱われるとある.

ファイルに問題があるのかと考え,ls -1ls -1r に替えてファイル順を逆にして metainfo を生成してみた.その結果,まったく同じ位置の piece から後がダウンロードされなかった.このことから,ファイルに問題はないと考えた.

また,おもしろい現象があった.Buildtorrent は -L オプションの引数で piece の大きさを 2引数 bytes に指定できる.これを 17 と 20 にして試したところ,17 では 16384 番とその後の piece が,20 では 2048 番とその後がダウンロードできなかった.つまり,丁度 2 Gbytes = 231 bytes までしかダウンロードできなかった.このファイル群とは別のファイル群でも試した.合計 2 GB 未満のファイル群 2 点はいずれもダウンロードできた.合計 2 GB を超えるファイル群 2 点はいずれも丁度 2 GB となる piece までしかダウンロードできなかった.

Buildtorrent で生成した metainfo 中のハッシュ値が誤っているのだろうか.別の方法でハッシュ値を計算して比較してみよう.metainfo 中では,全ファイルをリストされる順序に連結したものを piece 長に切り分けたものに対する binary SHA1 ハッシュ値 20 bytes を piece 順に連結したものが格納される.以下の 2 つのプログラムを書いた.

#!/bin/bash

piecesize=262144

if [ $# -lt 1 ]; then
    echo "Usage: $0 file ..." >&2
    exit 1
fi

totallength=`cat "$@" | wc -c`
numberpieces=`expr ${totallength} / ${piecesize}`


for (( cursor=0; cursor<=${numberpieces}; cursor++ ))
do
    cursorbyte=`expr ${cursor} '*' ${piecesize}`
    cat "$@" ¥
        | tail -c `expr ${totallength} - ${cursor} '*' ${piecesize}` ¥
        | head -c "${piecesize}" ¥
        | openssl dgst -binary -sha1
done

もちろんこの bash のコードは効率が悪い.

#!/usr/bin/perl -w

use strict;
use Digest::SHA1 qw(sha1);

my @files = @ARGV;
my $size = 2**18;

my $carryover;
foreach my $file ( @files ) {
    open( FILE, "< $file" ) or next;
    while( my $length = read( FILE, my $piece, ( $carryover? $size - length( $carryover ): $size ) ) ) {
        $carryover and $piece = $carryover . $piece;
        undef( $carryover );
        if ( length( $piece ) < $size ) {
            $carryover = $piece;
            last;
        }
        print sha1( $piece );
    }
    close( FILE ) or die;
}
$carryover and print sha1( $carryover );

perl モジュール Digest::SHA1 での計算結果は,Buildtorrent のものと等しかった.bash から呼んだ openssl dgst での計算結果も等しかった.

また,ついでだが,Buildtorrent で必須とされているためダミーの値を与えていた Announce URL は,生成した metainfo を sed 's|8:announce30:dht://trackerless.dht/announce||' して除去できることがわかった.metainfo ファイルは bencode (びーえんこーど) されている.

つづく.