System.Net.Dns.GetHostEntryを使った名前解決の問題
.NET 2.0 以降でホスト名の名前解決を行うには System.Net.Dns クラスにある GetHostEntry メソッドを用いる。(自分のマシンのIPアドレスを取得する方法は ローカルマシンのIPv4アドレスを取得する を参照)
パラメータにホスト名を渡すとDNSサーバーにクエリが送られ、AddressListプロパティにIPアドレスのリストが格納される(ホスト名とIPアドレスは1対1とは限らない)。通常は最初のアドレスを使えばよいので、下記のようにすれば、ホスト名からIPアドレスを得られる。
1 2 |
string host = "hogehoge"; IPAddress address = Dns.GetHostEntry(host).AddressList[0]; |
では、このhostにIPアドレスを渡したらどうなるだろう?
普通はそのままのIPアドレスを返してほしいところだ。つまり、下記のようなコードを書けば、addressには”192.168.1.3″が返ってほしいわけである。
1 2 3 |
string host = "192.168.1.3"; IPAddress address = Dns.GetHostEntry(host).AddressList[0]; // addressの中身は? |
実際、このメソッドはこういう仕様になっていて、上のコードではaddressに”192.168.1.3″が返るようになっている。
ただし、一つ問題がある。これがうまくいくのは同セグメントかつ、DNS サーバーが適切に設定されている場合のみである。
つまり、実行したパソコンの IP アドレスが192.168.1.1/24 だとして、GetHostEntryメソッドに渡すアドレスが192.168.1.3/24の場合はうまくいくのだが、192.168.2.3/24の場合にはうまくいかない。SocketException例外で”そのようなホストは存在しません。”と言われる。
おそらく別セグメントの場合はDNSサーバーに問い合わせに行くのだが、DNSサーバーが名前解決をできずに例外となるのだろう。なお、この問題は192.168.2.3がDNSサーバーに登録されている場合は発生しない。また、この症状が見られるのはVista以降のみでXPの場合は発生しなかった。
ともかくも最初に示したコードでは、別セグメントのIPアドレスが渡されたときに通信ができない事態があり得る。これを避けるには下記のようにIPアドレスかどうかを事前に判断してやればよい。
1 2 3 4 5 |
IPAddress address; if (!IPAddress.TryParse(host, out address)) { address = Dns.GetHostEntry(host).AddressList[0]; } |
IPAddress.TryParseは渡した文字列をIPAddressクラスのインスタンスに変換するメソッドである(名前解決はしない)。このメソッドはint.TryParseなどと同じように変換の可否をboolで返すので、失敗した場合(ホスト名の場合)は、Dns.GetHostEntryメソッドによって、名前解決してやればよい。
これでaddressには無事、希望のIPアドレスが得られるはずである。
That’s it!
(この記事は移植記事です。)