Delphi/C++BuilderでmacOS/iOS向けの実機デプロイやリモートデバッグに必須のPAServerとIDEの通信を覗いてみよう

これは [Delphi Advent Calendar 2016](http://qiita.com/advent-calendar/2016/delphi) 18日目の記事です。

# PAServerとは何か？

macOS / iOS 向けのアプリケーション開発を行う際には、当然ながら macOS が動く機材が必要です。そして XCode を macOS 上にインストールします。

さて、Delphi / C++Builer は 2011年以降のリリースで macOS や iOS のアプリケーション開発に対応しています(Androidにも対応しているけど、今回はその説明はナシ)。しかし Delphi / C++Builder の IDE やコンパイラは Windows 上だけで動きます。よって、作成したコードをコンパイルするには「macOS にセットアップした XCode や macOS SDK、iOS SDK の情報」を用いてクロスコンパイル環境をWindows上に構築することが必要です。また実機へのデプロイも macOS が必要です。iOS アプリを Windows から iOS に直接デプロイすることはできないのですから。

このために、Delphi / C++Builder で macOS や iOS 向けのアプリケーション開発を行う場合には「macOS 上で動作する中継サーバ」であるPAServerを介してデプロイします。

PAServer が提供する機能は次のとおりです。

+ macOS や iOS の SDK 情報を Delphi/C++Builder に対して配布する
+ Delphi/C++Builderでビルドしたバイナリを macOS や iOS に対してデプロイする
+ macOS や iOS に対してデプロイしたバイナリをDelphi/C++BuilderのIDEからデバッグ実行する

このときにDelphi/C++BuilderのIDEとPAServerの間の通信はTCPで行われます。

なお、余談ではありますが PAServer には Windows 版もあります。Windows向けの開発で開発機とテスト機が違う場合に PAServer だけをテスト機にインストールしてリモートデプロイしたり、リモートデバッグしたりできます。例えばタブレット端末向けのアプリをテストする場合に開発環境一式をターゲット機材にインストールするのは非現実的です。しかしPAServerだけを入れておけば、IDE側でビルドと実行を指示するだけでOKです。ビルドしたバイナリを転送してリモートで実行する処理はIDEとPAServerの間でやってくれるので、手間いらずです。

# IDEとPAServerの通信ってどうなってんの？

さて、ここで一つの疑問が出てこないでしょうか。「IDEとPAServerの間の通信って、どうなってるの？」とね。

そこで、今回の記事では IDE と PAServer の間の通信を覗いてみることにします。

## どうやって覗くのか？
TCP/IPの通信を覗き見する方法は主に３通りあります。

+ netsh trace
+ tcpdump
+ Wireshark

netsh trace は Windows7 以降の Windows でサポートされているパケットキャプチャです。環境を汚すことなしに使えるので、本番機で使うのに向いていますね。取得したデータは [Microsoft Message Analyzer](http://www.microsoft.com/en-us/download/details.aspx?id=44226) で解析できますし、あるいは Microsoft Message Analyzer で cap 形式に変換すれば Wireshark(後述) で扱えます。

tcpdump は UNIX で標準的に使われているパケットキャプチャです。グラフィカルなUIこそありませんが、リモートのサーバ上でデータキャプチャするのに使えます。

そして Wireshark ですが、オープンソースのパケットキャプチャです。公式サイトでは Windows / macOS 向けのバイナリが配布されています。GUI があるので使い易いです。また Linux の多くのディストリビューションでは標準の配布パッケージに含まれています。2006年までは Ethereal という名前で配布されていましたので、もしかするとこちらの名前で覚えている方もいらっしゃるかと思います。

さて、netsh trace や tcpdump でガチに行ってもよいのですが、GUIがあるほうがラクなので、まずはWiresharkを使ってみましょう。

# Wireshark をインストールする

## macOSにインストールする場合の注意
macOS 向けインストールでは注意点はありません。

インストールは https://www.wireshark.org/download.html から .dmg をダウンロードすることもできますし、また、Homebrew でもインストール可能です。ただしGUI版をHomebrewでインストールするには --with-qt とかのオプションをつける必要があります。

今回は macOS 側に .dmg 版をインストールしてみることにします。

## （参考）Windowsにインストールする場合の注意
**WindowsでWiresharkを使う場合はOSにパケットキャプチャ用のドライバをインストールする必要があります**。

パケットキャプチャ用のドライバやWiresharkが Delphi/C++Builderの実行やビルドに何か影響を及ぼすとは思わないのですが、**マルチプラットフォームの開発環境をインストールした環境に何かをインストールするなら、簡単に手戻りできる環境の場合に限ってインストールしたほうが良いと思います（インストール前に仮想マシンのスナップショットをとっておくなど）**。大抵のマルチプラットフォーム向けの開発環境はインストール容量が大きいことが多く、環境構築に時間がかかることがあります。意図しない要因で環境が壊れるとリカバリーが辛いので、簡単にリカバリーできるようにしておくことはとても大切ですよね。

**そしてパケットキャプチャ用のドライバは npcap をインストールするが便利と思います**。Wireshark に WinPCap が付属していますが、WinPCap は localhost 宛てのパケットをキャプチャできないのです。今回の要件では localhost 宛てのパケットキャプチャは不要ですが、Delphi/C++Builder で RAD Server のリソース開発を行うなど、ネットワーク通信が絡む処理の開発では生のパケットを覗き見したい場合があるはずです。しかし RAD Server の開発版サーバとクライアントを同一機材上で動かす場合は通信経路が localhost で完結してしまいますから、WinPCap では覗き見ることができないのです。

**npcap をインストールする際の注意点はインストーラ実行時のウィザードの Installation Option で "Install Npcap in WinPCap API-compatible Mode" にチェックを入れておくことです**。これで WinPCap 互換の API がインストールされますので、Wireshark から利用できるようになります。

さて npcap の入手ですが、ポートスキャナとして有名な nmap 関連のプロジェクトであり、GitHub でソースコードやバイナリが配布されています
https://github.com/nmap/npcap/releases

npcap をインストールしたら Wireshark の Windows 版インストーラを入手します。macOS版と同様に https://www.wireshark.org/download.html からダウンロードできます。

# Wireshark でキャプチャしてみる
## 起動してみる
起動するとこんな画面が出ます。ここでインタフェースを指定すれば、指定したインタフェースのパケットがキャプチャできます。全部のパケットをキャプチャすると分量が多すぎて鬱になりかねないので、キャプチャフィルタに "tcp port 64211" などと指定して、必要な通信だけに絞ってキャプチャするようにします。

![image](https://qiita-image-store.s3.amazonaws.com/0/149350/63d709ef-37d9-5a4d-02c4-bc76088fdb0d.png)

VMware FUSION をお使いの方で、ここでキャプチャインタフェースに vmnet* が出ていなかったり、キャプチャできない場合はお使いの Wireshark が古い可能性がありますので、最新版に入れ替えてみてください。最新版でも vmnet* がキャプチャできない場合は「VMware　FUSION に含まれる vmnet-sniffer を用いて FIFO 経由でキャプチャする」という workaround があります。
http://blog.mcchan.io/wireshark-vmfusion

## キャプチャを開始する

NICを選択してパケットキャプチャを開始すると、こんな画面に切り替わります。
![image](https://qiita-image-store.s3.amazonaws.com/0/149350/bb89a20c-5a5e-c165-79b4-eae54fbf8b8f.png)

さて、Delphi/C++BuilderのIDE側からPASererに接続してみましょう。[ツール]=>[オプション]=>[接続プロファイルマネージャー]から接続先のPAServerを設定して、接続テストを行ってみます。
![image](https://qiita-image-store.s3.amazonaws.com/0/149350/d19e8d6c-5950-72a4-9d21-df353e8645a4.png)

すると Wireshark の画面表示がこのように変わります。上から順番に、キャプチャしたパケットの一覧、選択中のパケットの構造表示、パケットの16進ダンプ表示、です。
![image](https://qiita-image-store.s3.amazonaws.com/0/149350/db4af650-11a5-5a51-c96f-2bf5a3eef1d6.png)

## キャプチャしたデータを眺めてみる

前述のスクリーンショットでは8つめのパケット (IDE から PAServer へのリクエスト送出）を選択表示しています。これを見たときに、ムムッと思う点はないでしょうか。

16進ダンプ表示を見ると、ASCII表示の箇所が JSON っぽく見えませんか？

そうなのです。じつは PASever との通信には JSON 形式のデータが用いられているのですよ！

さらによーくみると、"DataSnap" という文字も見えてきませんか？

とても気になるので、少し脱線して PAServer のバイナリをざっくりと調べてみましょう。

## PAServer のバイナリを調べてみる

では、以下のようにコマンド実行してみましょう。

```
$ strings /Applications/PAServer-18.0.app/Contents/MacOS/paserver | grep -i Datasnap | sort | uniq -c | sort -n | tail
```

これは「PAServer のバイナリの中の可読文字に含まれる Datasnap の文字を大文字小文字を無視して抽出し、その出現件数の上位10件を件数の少ない順に並べる」という処理を行っています。

その結果はこんな感じ。見事に Datasnap ですねえ。

![image](https://qiita-image-store.s3.amazonaws.com/0/149350/a5e7f449-fb78-276d-3d10-0a4885c83f6d.png)

というわけで、IDE と PAServer の通信は DataSnap で実装されておるのですね。

DataSnap は Delphi / C++Builder でサーバクライアント型の仕組みを実装するフレームワークの一つです。詳細は以下のリンク等を見ていただくのがよいと思います。

http://docwiki.embarcadero.com/RADStudio/Berlin/ja/DataSnap_%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E9%96%8B%E7%99%BA

## IDEとPAServerのやりとりの例
さて、PAServer と IDE のやりとりはかなり細かく行われているのですが、一つの例をあげると、こんなやりとりがあります。この例では PAServer 側でハンドリングできるターゲットOSの確認や、その際の出力先のパス情報が交換されているようです。

```JSON:IDEからPAServerへの問い合わせ
{
  "method": "execute",
  "params": [
    {
      "handle": [
        3
      ]
    },
    {
      "data": [
        11,
        %OSX32
      ]
    }
  ]
}
```

```JSON:PAServerからIDEへの返答
{
  "result": [
    {
      "rows": [
        0
      ]
    },
    {
      "data": [
        86,
        %OSX32aZaG/Users/kazuhiro.inoue/PAServer/scratch-dir/Kazuhiro.Inoue-VMware-local/`
      ]
    }
  ]
}
```

このやりとりのポイントは、"method":"xxxx" に対して "result" で返答が来る点ですね。他のやりとりを見ると "result" ではなく "callback" が返される処理もあります。


## JSON ならば Wireshark でなくてもよかったのでは？
まあそれはそうなのですけど、やってみないとわからない！

では Wireshark を使わない場合はどうするか？ tcpdump なり vmnet-sniffer なりでキャプチャしたものを別途解析できます。

たとえば VMware ならこんなふうにキャプチャします。

```
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-sniffer -e -w pcap-vmnet8 vmnet8
```

![image](https://qiita-image-store.s3.amazonaws.com/0/149350/e18aa56b-ea8e-8e94-3d38-188ce839bd29.png)

これを次のように処理すれば、キャプチャした生データから可読文字だけを抜き出せます。JSON ならばこの程度の方法でも十分に意味のある解析が行えることでしょう。

```
sudo cat pcap-vmnet8 | strings
```

![image](https://qiita-image-store.s3.amazonaws.com/0/149350/560b771b-4511-40c4-a0a2-6a57378affea.png)

ただしこれでは見づらいので、私がこの手の解析を行うときは自作の着色ツールを使って出現する文字を色分けしてみます。
![image](https://qiita-image-store.s3.amazonaws.com/0/149350/c6235d21-ac15-0d0c-0c66-5e26acbb0839.png)


というわけで、PAServer には DataSnap が使われていることがわかりました。ここらへんのやりとりに興味が出た方は、ぜひご自身でハックしてみてください。

# 次回予告？
Wireshark などのパケットスニファーを使えば、ネットワーク経由の通信内容を覗き見ることができますが、ではHTTPSで暗号化された通信を覗き見ることはできるでしょうか？ これを行うには、Man in the middle という手法が必要です。Advent Calendar の枠はもう空きがありませんが、別の機会に HTTPS の通信を覗き見る手法について書いてみるかも。