以前 (古早以前) 寫過一個簡單的 LIBRARY,就是去抓現在連上網頁的 CLIENT IP,然後簡單的套上 NET MASK,看看是不是在指定的網段內? 是的話就作些特別的處理 blah blah... 原本的 code 有點雜,我精簡之後變這樣,如果是 192.168.2.0 / 24 這範圍內的使用者連到這網頁,就會顯示 "Is Intranet? YES" ... 夠簡單吧? (怎麼連幾篇都這種不入流的 sample code ...)
這段 code 一直都運作的很好,沒碰過什麼大問題,不過就是把 IP address 切成四個 bytes, 然後利用位元運算併成 unsing integer, 方便跟後面的 netmask 作 bits and ...。不過某日興沖沖裝好 vista x64 + IIS7 之後發現,程式竟然不動了!?
先來看一下原始碼:
1: <%@ Page Language="C#" Trace="true" %>
2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3: <script runat="server">
4:
5: protected void Page_Load(object sender, EventArgs e)
6: {
7: this.Trace.Warn(System.Net.IPAddress.Parse(this.Request["REMOTE_HOST"]).AddressFamily.ToString());
8: this.IPLabel.Text = this.IsInSubNetwork(
9: "192.168.2.0",
10: "255.255.255.0",
11: this.Request.ServerVariables["REMOTE_HOST"]) ? ("YES") : ("NO");
12: }
13:
14:
15: private bool IsInSubNetwork(string network, string mask, string address)
16: {
17: uint netval = _IP2INT(network);
18: uint maskval = _IP2INT(mask);
19: uint addval = _IP2INT(address);
20:
21: return (netval & maskval) == (addval & maskval);
22: }
23:
24: private uint _IP2INT(string address)
25: {
26: string[] segments = address.Split('.');
27:
28: uint ipval = 0;
29: foreach (string segment in segments)
30: {
31: ipval = ipval * 256 + uint.Parse(segment);
32: }
33:
34: return ipval;
35: }
36:
37:
38: </script>
39:
40: <html xmlns="http://www.w3.org/1999/xhtml">
41: <head runat="server">
42: <title>Untitled Page</title>
43: </head>
44: <body>
45: <form id="form1" runat="server">
46: <div>
47: Is Intranet? <asp:Label ID="IPLabel" runat="server" />
48: </div>
49: </form>
50: </body>
51: </html>
後來追了半天才意外發現問題出在這... 打開 ASP.NET Trace, 看一下 REMOTE_ADDR 到底抓到啥子東西?
嘖嘖嘖,搞半天原來是 Vista 預設把 IPv6 給開了起來,IIS7 / DevWeb 都中獎,直接回報 IPv6 格式的 IP Address 回來... 怎麼解? 這種問題說穿了就不值錢,強迫用 IPv4 就好。我試過幾種可行的方式,有:
- 直接用 IPv4 的位址連線: 這簡單,以我來說,URL 從 http://localhost/default.aspx 改成 http://192.168.100.40/default.aspx 就好了。不過這樣對 DevWeb 就沒用了,DevWeb 只接受來自 localhost 的連線...
- 改 IIS 設定,直接綁到 IPv4 的位址,不過這招試不出來,似呼沒啥用,localhost 不會連到 192.168.100.40,而我直接打這 IP 的話就會變成範例1...
- 改 c:\windows\system32\drivers\etc\hosts
無意間 PING 看看 localhost, 才發現連 localhost 都被對應到 IPv6 了...
打開 C:\windows\system32\drivers\etc\hosts 這檔案看一看,果然...
把 IPv6 那行拿掉後再試試 ping localhost ...
耶! 這次 IP 就變成 IPv4 的了... 開 IE, 連 http://localhost/default.aspx 看看,it works!
因為這招是直接把 localhost 對應到 127.0.0.1,因此對於鎖 localhost 的 WEBDEV 也可以用。
- 大絕招: 直接關掉 IPv6 ...
真是個沒品的傢伙,打不過就來這套...
這樣也可以...
碰到這種怪問題,一時之間還熊熊不知道是那裡掛掉,還真是麻煩... 特地記一下這篇,讓一樣吃過 IPv6 苦頭的人參考一下。至於怎樣作才對? 當然是用 "正規" 的方式來處理 IP Address... System.Net.IPAddress 類別包含一個靜態方法: IPAddress Parse(string ipaddress), 用它可以把字串格式的 IP 換成這個類別的 instance, 用它內建的 property: AddressFamily,看看值是 enum 型態的 InterNetwork 還是 InterNetworkV6 就知道了,不要像我當年年少不更事一樣,自己硬去拆字串... Orz