WiresharkでSOAPメッセージをトレースする、コネクションエラー、通信しているXMLの内容を確認する。検証用SOAPクライアントならPython-Zeep熱烈推奨


環境

  • Ubuntu18.04 ※Windows10のVMWare上で動作する仮想マシン
  • Wireshark 2.6.10 (Git v2.6.10 packaged as 2.6.10-1~ubuntu18.04.0)
  • Python3
    • zeep 3.4.0

トレース方法

結論から書くとWiresharkで以下のフィルタを使います。

ip.addr == サーバIPアドレス && (xml || http || https || tcp)

Wiresharkではデフォルトではネットワーク上を流れている大量の無関係の通信がキャプチャされます。

まずは、SOAPサーバのIPアドレスをPINGで取得してフィルタします。
上記の指定方法だと、当該IPアドレスが送信、受信のどちらかであればキャプチャされます。

次にSOAP関連通信をフィルタします。
全てのSOAPメソッドに該当するかわかりませんが、私が利用したSOAPサービスは以下の順番で処理を行います。

  • WSDL取得
  • SOAPメソッド実行

この時の通信内容は以下のようになっています。これに基づいてHTTP(S)をトレースします。

# 処理 プロトコル メソッド ポート 備考
WSDL取得 HTTP GET 80
SOAPメソッド実行 HTTP/XML POST 任意(WSDLで定義)

またHTTPリクエストの前にTCPでコネクション確立(SYN,ACKなど)を行いますので、TCPも追加します。

参考/コネクションエラーの分析

WSDL取得は成功しても、メソッド実行時にコネクションが確立できない場合があります。
これはHTTP/POST通信(ポート含む)がFirewall等で制限されている場合などに発生します。
コネクションエラー等の問題が発生している場合、まずはTCPでコネクション確立(SYN->ACK)できているか?を確認するとよいかと思います。
SYN->RSTならコネクション確立できていません。
info部分でポート番号も確認できるのでSOAPサーバを管理している人に問い合わせしましょう。

以下はこの例の時のスクショです。赤枠で囲んだ部分がSYN -> RSTとなっているのが確認できます。

参考/通信しているXMLの内容確認

WiresharkでprotocolHTTP/XMLとなっているところを選択すると詳細ペインでXMLの内容を確認できます。

Soapクライアントを簡単に作成する方法/Zeep

Pythonを実行できるならZeepがおすすめです。

JavaでSOAPクライアントを作成することを考えてみましょう。
(WSDL定義の複雑さによりますが業務で使うくらいのサービスだと)wsimportで自動生成されるクラスは大量になります。
実行するにもコンパイルが必要ですし、ログを出したり、ステップ実行しながら状態を確認したい。となるとさらに面倒です。

Python Zeepなら事前のコード生成・コンパイルは不要です(WSDLから動的に内部的に生成している模様)。
ログは組み込みのprint、ステップ実行もこれまた組み込みのインタラクティブシェルで実現できます。

SOAPサービスの複雑さにもよりますが、Javaが準備に3時間かかるとしたらPython Zeepなら15分くらいでしょうか。
この15分の大半はメソッドの引数の型を調べて値をDictに設定する時間です。メソッドが引数をとらないのであればこの作業が不要なので数分でしょうか。
(JavaをDisっているわけではないです。実際に弊社パッケージのバックエンドはJavaが主体です)

たとえば以下のコードをインタラクティブシェルで逐次実行すればよいだけです。

from zeep import Client

# WSDLロケーション指定
wsdl = 'http://xxxxxxxxxxxx/SampleService.asmx?wsdl'
print('wsdl   => {}'.format(wsdl))

# Soapクライアント作成
client = Client(wsdl=wsdl)
# 生成されたSOAPクライアントの構造を表示
print('client=> {}'.format(dir(client))

# パラメタ設定
params = {
    "id":"XXX",
    "secret":"XXX",
    "number":"1234567890-001-001",
}
print('params => {}'.format(params))

# 実行
result = client.service.cancel(Request=params)

print('result => {}'.format(result))

トラブルシューティング

ネットワークがない

Wiresharkを一般ユーザで実行していませんか?この時に表示されるネットワークにはアクティブなネットワークが含まれません。
root権限を持つユーザで実行するとイーサネット用、ループバック用が表示されます。

【参考】一般ユーザでWireSharkを実行した時のネットワーク一覧

【参考】rootユーザでWireSharkを実行した時のネットワーク一覧