On the Wire:適合 API 開發人員的網路擷取工具

Google Data API 團隊 Lane LiaBraaten
2007 年 6 月

簡介

開發與網路服務互動的應用程式時,會遇到一連串獨特的問題。如果不知道傳送至伺服器的確切訊息,或收到的回應為何,就很容易感到沮喪。最難追蹤的錯誤,通常是我們認為要傳送至伺服器的內容,與實際透過線路傳送的內容不一致所致。

本文將介紹幾項工具,協助您更清楚瞭解網路上的資料,並加以運用。這類工具通常稱為「封包監聽器」,可擷取在網路介面中移動的所有網路封包。檢查這些封包的內容,以及傳送和接收封包的順序,是實用的偵錯技術。

範例:擷取公開動態消息

我正在為慈善騎乘活動組建自行車隊,並建立日曆來記錄資訊說明會、團隊募款和訓練騎乘等活動。我已將這個日曆設為公開,因此團隊成員和其他車友可以查看日曆並參與活動。我也想發送即將舉辦活動的電子報,因此我可以使用 Google 日曆資料 API 查詢這個日曆並擷取活動,不必從 Google 日曆網站複製資訊。

Google Calendar API 說明文件提供相關資訊,說明如何使用 RESTful Google Data API,以程式輔助方式與我的日曆互動。(編輯者附註:自第 3 版起,Google Calendar API 不再使用 Google 資料格式)。首先,請按一下日曆設定頁面上的 按鈕,取得日曆的活動動態消息網址:

http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic

Google 日曆說明文件為參考資料,我可以擷取及顯示日曆活動,如下所示,其中 PUBLIC_FEED_URL 包含活動動態消息網址。

CalendarService myService = new CalendarService("exampleCo-fiddlerExample-1");
final String PUBLIC_FEED_URL = "http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic";
URL feedUrl = new URL(PUBLIC_FEED_URL);
CalendarEventFeed resultFeed = myService.getFeed(feedUrl, CalendarEventFeed.class);

System.out.println("All events on your calendar:");
for (int i = 0; i < resultFeed.getEntries().size(); i++) {
  CalendarEventEntry entry = resultFeed.getEntries().get(i);
  System.out.println("\t" + entry.getTitle().getPlainText());
}
System.out.println();

這會產生日曆上活動的基本清單:

All events on your calendar:
    MS150 Training ride
    Meeting with Nicole
    MS150 Information session

上述程式碼片段會顯示日曆活動的標題,但我們從伺服器收到的其他資料呢?Java 用戶端程式庫無法輕鬆將動態饋給或項目輸出為 XML,即使可以,XML 也不是完整的故事。要求隨附的 HTTP 標頭呢?查詢是否經過 Proxy 或重新導向?如果作業較為複雜,這些問題就變得越來越重要,尤其是在發生錯誤時。封包側錄軟體可以顯示網路流量,回答這些問題。

tcpdump

tcpdump 是一種指令列工具,適用於類似 Unix 的平台,但也有稱為 WinDump 的 Windows 連接埠。與大多數封包監聽器一樣,tcpdump 會將網路卡設為任意模式,這需要超級使用者權限。如要使用 tcpdump,只要指定要監聽的網路介面,網路流量就會傳送至 stdout:

sudo tcpdump -i eth0

如果執行這項指令,您會看到各種網路流量,其中有些您甚至不認識。您可以將輸出內容轉送至檔案,然後 grep 該檔案,但這可能會導致檔案非常龐大。大多數封包擷取軟體都內建篩選機制,因此您只會擷取所需的內容。

tcpdump 支援根據網路流量的各種特徵進行篩選。舉例來說,您可以在下列運算式中插入伺服器的主機名稱,讓 tcpdump 只擷取通訊埠 80 (HTTP 訊息) 上往來伺服器的流量:

dst or src host <hostname> and port 80

對於符合篩選器運算式的每個封包,tcpdump 會顯示時間戳記、封包的來源和目的地,以及數個 TCP 標記。這項資訊很有價值,因為它會顯示封包的傳送和接收順序。

查看封包內容通常也很有用。「-A」旗標會指示 tcpdump 以 ASCII 格式列印每個封包,顯示 HTTP 標頭和訊息主體。「-s」標記用於指定要顯示的位元組數 (其中「-s 0」表示完全不要截斷郵件內文)。

將所有內容整合在一起,即可得到下列指令:

sudo tcpdump -A -s 0 -i eth0 dst or src host <hostname> and port 80

執行這項指令,然後執行上述簡短的 .Java 範例,您會看到這項作業涉及的所有網路通訊。在您看到的流量中,會出現 HTTP GET 要求:

22:22:30.870771 IP dellalicious.mshome.net.4520 > po-in-f99.google.com.80: P 1:360(359) ack 1 win 65535
E.....@....\...eH..c...P.=.....zP......GET /calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic HTTP/1.1
User-Agent: exampleCo-fiddlerExample-1 GCalendar-Java/1.0.6 GData-Java/1.0.10(gzip)
Accept-Encoding: gzip
Cache-Control: no-cache
Pragma: no-cache
Host: www.google.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

您也會看到包含 Google 產品資料動態饋給的 200 OK 回覆訊息。請注意,動態饋給會分成四個封包:

22:22:31.148789 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 1:1431(1430) ack 360 win 6432
E...1 ..2.I.H..c...e.P.....z.=.:P..M...HTTP/1.1 200 OK
Content-Type: application/atom+xml; charset=UTF-8
Cache-Control: max-age=0, must-revalidate, private
Last-Modified: Mon, 11 Jun 2007 15:11:40 GMT
Transfer-Encoding: chunked
Date: Sun, 24 Jun 2007 02:22:10 GMT
Server: GFE/1.3

13da
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gCal='http://sc
hemas.google.com/gCal/2005' xmlns:gd='http://schemas.google.com/g/2005'><id>http
://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.goo
gle.com/public/basic</id><updated>2007-06-11T15:11:40.000Z</updated><category sc
heme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2
005#event'></category><title type='text'>MS150 Training Schedule</title><subtitl
e type='text'>This calendar is public</subtitle><link rel='http://schemas.google
.com/g/2005#feed' type='application/atom+xml' href='http://www.google.com/calend
ar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic'></
link><link rel='self' type='application/atom+xml' href='http://www.google.com/ca
lendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic
?max-results=25'></link><author><name>Lane LiaBraaten</name><email>api.lliabraa@
gmail.com</email></author><generator version='1.0' uri='http://www.google.com/ca
lendar'>Google Calendar</generator><openSearch:totalRe


22:22:31.151501 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 1431:2861(1430) ack 360 win 6432
E...1!..2.I.H..c...e.P.......=.:P.. 2...sults>3</openSearch:totalResults><openSe
arch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch
:itemsPerPage><gd:where valueString=''></gd:where><gCal:timezone value='America/
Los_Angeles'></gCal:timezone><entry><id>http://www.google.com/calendar/feeds/24v
j3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/dgt40022cui2k3j
740hnj46744</id><published>2007-06-11T15:11:05.000Z</published><updated>2007-06-
11T15:11:05.000Z</updated><category scheme='http://schemas.google.com/g/2005#kin
d' term='http://schemas.google.com/g/2005#event'></category><title type='text'>M
S150 Training ride</title><summary type='html'>When: Sat Jun 9, 2007 7am to 10am

&amp;nbsp; PDT&lt;br&gt;   &lt;br&gt;Event Status:     confirmed</summary><conte
nt type='text'>When: Sat Jun 9, 2007 7am to 10am&amp;nbsp; PDT&lt;br&gt;   &lt;b
r&gt;Event Status:     confirmed</content><link rel='alternate' type='text/html'
 href='http://www.google.com/calendar/event?eid=ZGd0NDAwMjJjdWkyazNqNzQwaG5qNDY3
NDQgMjR2ajNtNXBsMTI1YmgyaWpiYm5laDk1M3NAZw' title='alternate'></link><link rel='
self' type='application/atom+xml' href='http://www.google.com/calendar/feeds/24v
j3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/dgt40022cui2k3j
740hnj46744'></link><author><name>MS150 Training Schedule</name></author><gCal:s
endEventNotifications value='false'></gCal:sendEventNotifications></entry><entry

><id>http://www.google.com/cal

22:22:31.153097 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 2861:4291(1430) ack 360 win 6432
E...1#..2.I.H..c...e.P.......=.:P.. ....endar/feeds/24vj3m5pl125bh2ijbbneh953s%4
0group.calendar.google.com/public/basic/51d8kh4s3bplqnbf1lp6p0kjp8</id><publishe
d>2007-06-11T15:08:23.000Z</published><updated>2007-06-11T15:10:39.000Z</updated
><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.g
oogle.com/g/2005#event'></category><title type='text'>Meeting with Nicole</title

><summary type='html'>When: Mon Jun 4, 2007 10am to 11am&amp;nbsp; PDT&lt;br&gt;
  &lt;br&gt;Where: Conference Room B &lt;br&gt;Event Status:     confirmed</summ
ary><content type='text'>When: Mon Jun 4, 2007 10am to 11am&amp;nbsp; PDT&lt;br&
gt;  &lt;br&gt;Where: Conference Room B &lt;br&gt;Event Status:     confirmed

&lt;br&gt;Event Description: Discuss building cycling team for MS150</content><l
ink rel='alternate' type='text/html' href='http://www.google.com/calendar/event?
eid=NTFkOGtoNHMzYnBscW5iZjFscDZwMGtqcDggMjR2ajNtNXBsMTI1YmgyaWpiYm5laDk1M3NAZw'
title='alternate'></link><link rel='self' type='application/atom+xml' href='http
://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.goo
gle.com/public/basic/51d8kh4s3bplqnbf1lp6p0kjp8'></link><author><name>MS150 Trai
ning Schedule</name></author><gCal:sendEventNotifications value='false'></gCal:s
endEventNotifications></entry><entry><id>http://www.google.com/calendar/feeds/24
vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/va41amq3r08dhh
kpm3lc1abs2o</id><published>20


22:22:31.190244 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: P 4291:5346(1055) ack 360 win 6432
E..G1$..2.K.H..c...e.P.....<.=.:P.. ....07-06-11T15:10:08.000Z</published><updat
ed>2007-06-11T15:10:08.000Z</updated><category scheme='http://schemas.google.com
/g/2005#kind' term='http://schemas.google.com/g/2005#event'></category><title ty
pe='text'>MS150 Information session</title><summary type='html'>When: Wed Jun 6,
 2007 4pm to Wed Jun 6, 2007 5pm&amp;nbsp; PDT&lt;br&gt;   &lt;br&gt;Event Statu
s:     confirmed</summary><content type='text'>When: Wed Jun 6, 2007 4pm to Wed
Jun 6, 2007 5pm&amp;nbsp; PDT&lt;br&gt;   &lt;br&gt;Event Status:     confirmed<

/content><link rel='alternate' type='text/html' href='http://www.google.com/cale
ndar/event?eid=dmE0MWFtcTNyMDhkaGhrcG0zbGMxYWJzMm8gMjR2ajNtNXBsMTI1YmgyaWpiYm5la
Dk1M3NAZw' title='alternate'></link><link rel='self' type='application/atom+xml'
 href='http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.c
alendar.google.com/public/basic/va41amq3r08dhhkpm3lc1abs2o'></link><author><name
>MS150 Training Schedule</name></author><gCal:sendEventNotifications value='fals
e'></gCal:sendEventNotifications></entry></feed>

這項輸出內容包含所有 HTTP 標頭和內容,以及多個隱晦的 TCP 旗標。這裡會顯示所有資料,但可能難以閱讀和理解。您可以使用多種圖形工具,輕鬆查看這項資料。

WireShark (原稱 Ethereal)

Wireshark 的螢幕截圖
WireShark 會以多種方式顯示網路流量。

WireShark 是以 libpcap 建構的圖形化工具,與 tcpdump 使用的程式庫相同,適用於 Linux、Mac OS X 和 Windows。WireShark 的 GUI 提供多種解讀封包擷取資料和與之互動的新方式。舉例來說,從網路介面擷取封包時,系統會根據封包使用的通訊協定,以不同顏色顯示封包。您也可以依時間戳記、來源、目的地和通訊協定排序流量。

如果您在封包清單中選取資料列,Wireshark 會在封包標頭中以易讀樹狀結構顯示 IP、TCP 和其他通訊協定專屬資訊。畫面底部也會以十六進位和 ASCII 格式顯示資料。

雖然 WireShark 的視覺化特性有助於瞭解網路流量,但您在大多數情況下仍會想要篩選網路流量。WireShark 具有強大的篩選功能,支援數百種通訊協定。

提示:如要查看可用的通訊協定及建立複雜的篩選器,請按一下 WireShark 視窗頂端的 按鈕。

如要重新建立上述 tcpdump 範例中使用的篩選器,請將下列運算式插入 WireShark 篩選器方塊:

ip.addr==<your IP address> && tcp.port==80

或者,運用 WireShark 的 HTTP 知識:

ip.addr==<your IP address> && http

這樣一來,擷取結果就會經過篩選,只顯示與 Google 日曆伺服器互動時涉及的封包。您可以點選每個封包查看內容,並拼湊出交易。

提示:你可以對其中一個封包按一下滑鼠右鍵,然後選擇「Follow TCP Stream」,在單一視窗中依序顯示要求和回應。

WireShark 提供多種儲存擷取資訊的方式。您可以儲存一個、部分或所有封包。如果您正在查看 TCP 串流,只要按一下「另存為」按鈕,即可只儲存相關封包。您也可以匯入 tcpdump 擷取的輸出內容,並在 WireShark 中查看。

問題:SSL 和加密

封包擷取工具的常見缺點是無法查看透過 SSL 連線加密的資料。上述範例存取的是公開動態饋給,因此不需要 SSL。不過,如果範例存取的是私人動態消息,用戶端就必須向 Google 驗證服務進行驗證,這時就需要 SSL 連線。

下列程式碼片段與上一個範例類似,但這裡的 CalendarService 會要求使用者的日曆中繼動態消息,這是需要驗證的私人動態消息。如要進行驗證,只要呼叫 setUserCredentials 方法即可。這個方法會觸發對 ClientLogin 服務的 HTTPS 要求,並從回應中擷取驗證權杖。之後,CalendarService 物件就會在所有後續要求中加入驗證權杖。

CalendarService myService = new CalendarService("exampleCo-fiddlerSslExample-1");
myService.setUserCredentials(username, userPassword);
final String METAFEED_URL = "http://www.google.com/calendar/feeds/default";
URL feedUrl = new URL(METAFEED_URL);
CalendarFeed resultFeed = myService.getFeed(feedUrl, CalendarFeed.class);

System.out.println("Your calendars:");
for (int i = 0; i < resultFeed.getEntries().size(); i++) {
  CalendarEntry entry = resultFeed.getEntries().get(i);
  System.out.println("\t" + entry.getTitle().getPlainText());
}
System.out.println();

請考量驗證及存取私人 Google Data API 資訊動態饋給所需的網路流量:

  1. 將使用者憑證提交至 ClientLogin 服務
    • 將 HTTP POST 傳送至 https://www.google.com/accounts/ClientLogin,並在訊息主體中加入下列參數:
      • 電子郵件:使用者的電子郵件地址。
      • Passwd - 使用者的密碼。
      • source - 用於識別用戶端應用程式。格式應為 companyName-applicationName-versionID。範例使用 ExampleCo-FiddlerSSLExample-1 這個名稱。
      • 服務 - Google 日曆的服務名稱為「cl」。
  2. 接收授權權杖
    • 如果驗證要求失敗,您會收到 HTTP 403 Forbidden 狀態碼。
    • 如果成功,服務的回應會是 HTTP 200 OK 狀態碼,加上回應主體中的三個長英數字元代碼:SIDLSIDAuthAuth 值是授權權杖。
  3. 要求私密日曆的 Metafeed
    • 將 HTTP GET 傳送至 http://www.google.com/calendar/feeds/default,並加上下列標頭:
    • Authorization: GoogleLogin auth=<yourAuthToken>
      

請試著執行這段程式碼片段,並在 WireShark 中查看網路流量 (使用「http || ssl」做為篩選條件)。您會看到交易中涉及的 SSL 和 TLS 封包,但 ClientLogin 要求和回應封包會加密在「應用程式資料」封包中。別擔心,接下來我們會介紹一個工具,這個工具可以實際揭露這項加密資訊。

Fiddler

Fiddler 是另一項圖形封包監聽工具,但運作方式與目前介紹的工具大不相同。Fiddler 會充當應用程式與您互動的遠端服務之間的 Proxy,有效成為中間人。Fiddler 會與應用程式和遠端網路服務建立 SSL 連線,解密來自某個端點的流量、擷取純文字,然後重新加密流量,再傳送出去。很抱歉,Fiddler 僅適用於 Windows,Mac 和 Linux 使用者無法使用。

注意:如要支援 SSL,必須使用 Fiddler 第 2 版和 .NET Framework 2.0 版。

在 Fiddler 中查看網路流量時,主要會使用「Session Inspector」分頁。如要偵錯 Google Data API 的問題,最實用的子分頁包括:

  • 標頭:以可收合的樹狀結構格式顯示 HTTP 標頭。
  • Auth - 顯示驗證標頭。
  • 原始:以 ASCII 文字顯示網路封包內容

提示:按一下 Fiddler 視窗左下角的 圖示,即可開啟及關閉擷取功能。

Fiddler 會使用 .NET Framework 設定網路連線,將 Fiddler 設為 Proxy。也就是說,您透過 Internet Explorer 或 .NET 程式碼建立的任何連線,預設都會顯示在 Fiddler 中。不過,由於 Java 設定 HTTP 代理伺服器的方式不同,因此上述 Java 範例的流量不會顯示。

在 Java 中,您可以使用系統屬性設定 HTTP Proxy。Fiddler 會在通訊埠 8888 上執行,因此如果是本機安裝,您可以新增下列程式碼行,讓 Java 程式碼將 Fiddler 做為 HTTP 和 HTTPS 的 Proxy:

System.setProperty("http.proxyHost", "localhost");
System.setProperty("http.proxyPort", "8888");
System.setProperty("https.proxyHost", "localhost");
System.setProperty("https.proxyPort", "8888");

如果您使用這些程式碼行執行範例,實際上會從 Java 安全性套件取得難看的堆疊追蹤記錄:

[java] Caused by:
  sun.security.validator.ValidatorException: PKIX path building failed:
  sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Fiddler 的螢幕截圖
Fiddler 可以解密及顯示 SSL 流量。

如果無法驗證 SSL 連線中伺服器傳回的憑證,就會發生這項錯誤。在這個情況下,不良憑證來自 Fiddler,而 Fiddler 充當中間人。Fiddler 會即時產生憑證,但由於 Fiddler 並非信任的簽發者,這些憑證會導致 Java 無法設定 SSL 連線。

注意:執行 Fiddler 時,您在 Internet Explorer 中建立的任何 SSL 連線都會觸發「安全性快訊」,詢問您是否要繼續操作,即使憑證有問題也一樣。您可以按一下「查看憑證」,查看 Fiddler 產生的憑證。

那麼該如何規避這項安全性例外狀況?基本上,您需要重新設定 Java 的安全架構,以信任所有憑證。幸好您不必重新發明輪子,請查看 Francis Labrie 的解決方案,並將 SSLUtilities.trustAllHttpsCertificates() 方法加到上述範例中。

將 Java 設為使用 Fiddler 做為 Proxy,並停用預設憑證驗證後,您就可以執行範例,並以純文字查看所有透過網路傳送的流量。別偷我的密碼!

請注意,這項驗證交易只是安全資料傳輸層 (SSL) 流量的一小部分範例。部分網路應用程式只使用 SSL 連線,因此如果無法解密資料,就無法偵錯 HTTP 流量。

結論

tcpdump 適用於 Linux、Mac OS X 和 Windows,如果您知道要尋找的內容,且只需要快速擷取,這就是絕佳工具。不過,有些圖形工具會以更容易理解的格式呈現網路流量。tcpdump 的選項和篩選功能比本文介紹的更多。如要完整瞭解 tcpdump 的功能,請輸入「man tcpdump」或在線上查看 tcpdump 手冊頁面

WireShark 也適用於 Linux、Mac OS X 和 Windows。Wireshark 內建支援數百種通訊協定,因此不僅適用於 HTTP 偵錯,也可用於許多應用程式。這份簡介僅粗略介紹了 WireShark 的眾多功能。如需更多資訊,請輸入「man wireshark」或造訪 WireShark 網站

Fiddler 也有許多實用功能,但最特別的是解密 SSL 流量的功能。詳情請造訪 Fiddler2 網站

這些封包監聽應用程式是工具箱中不可或缺的工具,細心的讀者會發現這些應用程式都是免費的!下次使用 Google API 時,如果發現任何可疑之處,請拿出其中一個網路分析器,仔細檢查傳輸線上的內容。如果找不到問題,隨時可以在討論群組中提問。附上相關的網路訊息,有助於其他人瞭解及診斷您的特定問題。

祝你一切順利,盡情享受嗅聞的樂趣!

資源