PythonでBitTorrent DHTネットワークに流れるメッセージを眺めるツール btsniff (libtorrentを利用) を試してみた

どうも、ryo_gridです。
10年前くらいにP2P研究会とかに顔を出されていた方々、読まれていたらこんにちは。
　
inaz2氏が2013年10月6日に行われた "すみだITセキュリティ勉強会" という会でのトークのために（?）かは不明ですが、 BitTorrent DHT ネットワークで飛んでいるメッセージを見てみる btsniff というツールを開発されていて(見られるのは自分に飛んできたメッセージのようですが)、githubリポジトリの更新が2年前とかだったので厳しいかと思いましたが、動いたので手順等々共有しようと思います。

- inaz2氏のスライド  
    - [Sniffing BitTorrent DHT～人はBTで何を落とすのか～](https://www.slideshare.net/inaz2/20131006r02-sniffing-bittorrentdht)


- githubリポジトリ  
    - https://github.com/inaz2/btsniff


ついでに、BitTorrent DHT ネットワーク が BitTorrent という 系？において、どういう役割をしているか、というところの【私の理解】を書いてみます。
なお、BitTorrent用語や、DHTって何？とかは解説しないので、ご注意下さい。
　
# 試す
## 環境
- Windows10 64bit での WSL Ubuntu

```shell
$ uname -a
Linux LAPTOP-T08A2ILS 4.4.0-17134-Microsoft #648-Microsoft Tue Mar 05 18:57:00 PST 2019 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/debian_version
stretch/sid
```

- Python 2.7.0 64bit

```shell
$ python --version
Python 2.7.2
```



## コード
- 以下を clone してきて用いました  
    - https://github.com/inaz2/btsniff
- ライブラリのロードがそのままだとうまくいかなかったので btsniff.py の先頭に以下を追加しました。
- (virtualenvで環境を切ったせいで、本来は必要ないのかもしれません)


```python:patch.py
from os import path
sys.path.append(path.dirname("/usr/lib/python2.7/dist-packages/"))
```


## 手順
まず、NAT内の環境であれば、NATにプログラムを動作させるマシンの6881番へのポートフォワーディングを設定（両方必要か不明だがUDPとTCP両方しておけば安心）
　
続いて、

```shell
sudo apt-get install -y python-libtorrent
git clone https://github.com/inaz2/btsniff.git
cd btsniff
※ここで上記のコード修正をbtsniff.pyに行う
wget http://releases.ubuntu.com/18.04/ubuntu-18.04.2-desktop-amd64.iso.torrent
python btsniff.py desktop-amd64.iso.torrent
```


すると標準出力になにやらいろいろ出ました。
![btsniff_stdout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12325/16c7fa3d-1c27-06b2-02eb-708c7342389c.png)
(グローバルIPアドレスが出力されているところは、削ってあります)
　
ちなみにCentOS 6 や 7 だと、上記での python-libtorrentに相当する rpm パッケージが 少なくとも yum で普通に入るというようにはなっていないようで、自前でビルドしようと思いましたがうまくいかなくて挫折しました（最初はCentOSなマシンで動かそうとした）。
とりあえず、Boostは魔境。

# 注意（かつ、知っている人がいたら教えていただきたい）
- "metadata_received_alert" というメッセージを受信して、torrentファイルの中身に対応する？情報を保存しておこう、というコードがありますが、私は数時間動作させて一度も受信しませんでした （ここのコードでどんなファイルが流れてるのか分かるのかなーと期待していたのですが）
- libtorrent の [reference](https://www.libtorrent.org/reference-Alerts.html#metadata_received_alert) は読みましたが、結局どういう時に受信するものなのかはよく分かっていません・・・
- 私の使い方が誤っている可能性もあります・・・

# btsniffがやっていること (追記)
- 後述のとおり、DHT ネットワークにトラッカーの役割を担ってほしいノードは、torrentファイルのハッシュ値を基にトラッカー役のノード群 (8ノード) を探します。で、そのメッセージが "dht_get_peers_alert"
- get_peersのメッセージにはtorrentファイルに対応するハッシュ値がくっついていて、それを指定して自身のダウンロードキューに追加 (add_torrent関数) することでも、ファイルのダウンロードが行える（みたい）なので、btsniffはそんな感じでキューに追加している（通常はtorrentファイルの中身を引数に渡して追加する)
- その結果として受信する（というか、おそらく自分で発生させて自分で受信している）のが "add_torrent_alert" と "torrent_added_alert"
- ハッシュ値指定だけでキューに追加された要求は、libtorrentがよしなに処理してくれるらしく、必要な情報が届くと "metadata_received_alert" を受信（？）する。このalertには 通常のtorrentファイルと同様の情報 (metadeta) がくっついているので、それを出力するなり、ロギングすると、DHT ネットワークを流れているファイルの情報を知ることができる、ことになる。ただし、ファイル名は空の場合もあると、libtorrentのサイトにはある
- libtorrentのビルド時のオプションによっては、ハッシュ値指定でのダウンロードが行えない場合があるらしい
    - https://libtorrent.org/manual.html#async-add-torrent-add-torrent

> You also need to specify either the ti (the torrent file), the info_hash (the info hash of the torrent) or the url (the URL to where to download the .torrent file from). If you specify the info-hash, the torrent file will be downloaded from peers, which requires them to support the metadata extension. For the metadata extension to work, libtorrent must be built with extensions enabled (TORRENT_DISABLE_EXTENSIONS must not be defined). 

- "metadata_received_alert" が受信されないのは、このせいかもしれない・・・・です

# torsniff だと torrent ファイルの収集できた! (追記2)
いろいろ実装をいじったり、libtorrentを自前でビルドしようとしてみたり（Windowsでやってみたが、結局またゴールにはたどりつけず）、他のプログラムを試したり、マグネットリンクを作ってちゃんとしたクライアント(Vuzeとか)にハンドルさせてみたり、とかしてみましたが、info-hash から 対応するファイル名を見る、というのはできなくてあきらめ気味だったのですが、以下の torsniff というプログラムはちゃんと動作しました!

https://github.com/fanpei91/torsniff　

環境は上記と同じ WSL Ubuntu で、releases の v0.1.0 のビルド済みバイナリをノーオプションで動かすだけで、うまいこと収集してくれました。（NATがある環境であればポート6881のフォワーディングは設定しておく必要はあり）

なお、大量にコネクションを張るようなので (BitTorrent DHT自体がそういうもの、という話な気はしますが) 、NATがタコだと、動作させているマシン以外の同一LANのマシン・デバイスも含めて、インターネットへの通信が不安定になったりするようなので、その点はご注意下さい。

・**torsniffが動作している様子**
![D66y4UNV4AAvfQX.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12325/299c2bb2-2a6c-35d0-6bd6-413f9c77232e.png)


# Bittorrent DHTネットワークとは
まず、以下に記述する内容は私の認識です。誤っている可能性も大いにあるのと思うので、ご注意いただき、また、誤りに気づかれた方はご指摘いただければ幸いです。

## 仕様

- 基本的には以下のBEP で定められているようです（今読んでいるところ...）。
  - http://www.bittorrent.org/beps/bep_0005.html  
- 他のBEPでIPv6対応のために拡張されてたりするようですが、そこらへんは各自で追ってみて下さい
- 関連して？、マグネットリンクについては以下のBEPで仕様が定められているようですが、DHTネットワークとどう連携しているのか、そもそも連携していないのか、までは追っていません。
    - http://www.bittorrent.org/beps/bep_0009.html
    - そもそも、マグネットリンクについて良く知りません・・・
- bep0005 とかでググると、以下のようなgithubのタグのページがヒットしたりしたので、この中にbtsniff より高機能な情報収集ツール？クローラ？スニッファー？があるかもしれないです
    - https://github.com/topics/bep0005
    - https://github.com/topics/bep0009

## 何するもの？
- 下のBitComet (BitTorrentクライアント) の説明を見るに、トラッカーの代替をしてくれるもののようである
    - [Torrentの作成](https://jp.bitcomet.com/faq/maker.htm)

## どんな仕組み？
- 以下はlibtorrentの作者であるArvid Norberg氏がBitTorrentの解説として[libtorrentのサイト](https://www.libtorrent.org/) に置いているスライドですが、BitTorrent DHT ネットワークについての解説もしてくれています
    - https://libtorrent.org/bittorrent.pdf
- 以下、私の理解したところです
    - あるファイルのシーダおよびリーチャ（既にファイル全部か、一部を持っていて、他のノードにピースを提供できるノード）がDHTネットワークの機能を有効にしている場合、そのファイル、というかそのファイルを入手するために参照したtorrentファイルのハッシュ(info-hash) を求めて、その値に近いノードIDを持つ8つのノードに、「俺、ピース持ってるで」と通知する
    - これを各々のシーダおよびリーチャがやると、結果的に、あるtorrentファイルに対応する8つのノードにトラッカーとして振り舞いうるための情報が集まる
    - torrentファイルを入手してファイルを手に入れたいノード（以降、ファイル欲しいノード、と呼称）が現れて、そのtorrentファイルの中のトラッカーのリストにDHT ネットワークが指定されていると、torrentファイルのハッシュ(info-hash) を求めて、DHTネットワークを介して上記の8つのノードの情報を入手し、それらから、シーダおよびリーチャの情報をもらう (つまり、これらの8つのノードがトラッカーの代わりになる) 
    - トラッカーおよびリーチャの「俺、ピース持ってるで」は、何によってタイミングを決めるかは分からないですが、繰り返し行われるようです。また、ファイル欲しいノード、による問い合わせも繰り返し行われるようです。

# 考えてみた
- DHT ネットワーク を飛びかっているメッセージから、共有されいているファイル名とかが見れたら面白そうだな、と考えたのがモチベーション
- しかし、トラッカーを代替するのがBitTorrent DHT ネットワークの役割だとすると、シーダおよびリーチャからの「俺、ピース持ってるで」の情報を中継するノードはもちろん、トラッカの役割を与えられるノードも、torrentファイルのハッシュ(info-hash)と、torrentファイルに対応するファイル群のサイズ、各ファイルのハッシュ値、ピースの情報やらが見えれば十分なはずで（ノード情報は置いておきます）、ファイル名なんぞいらないのでは？と思いました。（ファイル欲しいノードと、それにピースを提供するシーダおよびリーチャは知っているでしょうが）
- つまり、流れてるファイル名を見よう！とか思っても、そもそも、そういう情報は流れていないのでは？とか思いました
- ただし、マグネットリンクについては上記の考察？の範囲外です。

以上！