EnumInterface Windows版
前回は名前(ドメイン)に関連つけられたIPアドレスを取得する
プログラムを作りましたが、今回はインターフェースに関連つけられた
IPアドレスを取得するプログラムを作ります。
残念ながら、この機能はソケットAPI(RFC 3493)だけでは実現することが
できません。
Windows版ではGetAdaptersAddressesを使います。
ここで使用するソケットライブラリ(RFC 3493)の関数
バイナリ形式IPアドレス(IPv4:32bits IPv6:128bits)を文字列に変換する関数
const char *inet_ntop(int af, const void *src, char dst, socklen_t size);
この関数は、af アドレスファミリーのネットワークアドレス構造体 src を 文字列に変換します。
(バイナリ形式アドレスsrcはネットワーク・バイト・オーダーでなければなりません。
変換結果の文字列は、dst が指すバッファーにコピーされます。
呼び出し時に、このバッファーで利用できるバイト数を引き数 size に指定します。
プロジェクトの作成
ファイル→新規作成→VisualC++→空のプロジェクト
プロジェクト名:EnumInterface
「ソリューションのディレクトリを作成する」のチェックを外す。
プラットホームは64bits(x64)とする。
プロジェクト→プロパティの構成プロパティの文字セットが「マルチ バイト文字
セットを使用する」になっていることを確認してください。
プロジェクト→既存の項目の追加を選びstdThread.cpp, stdThread.hを追加します。
プロジェクト→新規の項目の追加を選びEnumInterface.cppを追加します。
GetAdaptersAddressesの使い方
ローカルコンピュータ上のアダプタに関連付けられているアドレスを取得します。
ULONG GetAdaptersAddresses( ULONG Family, ULONG Flags, PVOID Reserved,
PIP_ADAPTER_ADDRESSES AdapterAddresses, PULONG SizePointer );
ここでは、パラメータとして次のものを指定します。
Family : AF_UNSPEC
アダプタに関連付けられているIPv4とIPv6の両方のアドレスを取得
Flags : GAA_FLAG_INCLUDE_PREFIX
アダプタのIPアドレスプレフィックスのリストを取得
AdapterAddresses :
先ず NULLをセットし、実際に格納に必要なサイズを取得する。
SizePointerにサイズがセットされる。
次に得られたサイズ分のバッファを確保し再度呼び出す。
SizePointer : AdapterAddresses に指定したバッファのサイズ
成功した時の戻り値
AdapterAddressesにNULLを指定した時は、
ERROR_BUFFER_OVERFLOWが返ってくる。
SizePointer に必要なバッファサイスが格納される
AdapterAddresses に正しいサイズのバッファを指定した時は、
ERROR_SUCCESSが返ってくる。
AdapterAddressesに必要な情報が格納される。
AdapterAddressesに必要な情報が格納されたら次のようにして読み出します。
IP_ADAPTER_ADDRESSES構造体は、メンバに*Nextを持つリストなので、
これがNULLになるまで順番にたどっていくと、取得した情報を
読み出すことができます。
ここで使用するメンバ変数は、次のものです。
struct _IP_ADAPTER_ADDRESSES_LH *Next;
PWCHAR FriendlyName;
PIP_ADAPTER_UNICAST_ADDRESS_LH FirstUnicastAddress;
*NextをNULLになるまでリストを順番にたどっていくと取得した情報を
読み出すことができます。
FriendlyNameはアダプタの名称です。
UTF-16で格納されているので表示するにはS-JISに変換することが必要です。
FirstUnicastAddressはこのインターフェースに割り振られている
ユニキャスト情報のリストです。
これを順にたどるとユニキャストアドレスを読み出すことができます。
IP_ADAPTER_UNICAST_ADDRESS_LH構造体を順にたどり、ユニキャストの
情報を読みだします。
ここで使用するメンバ変数は、次のものです。
SOCKET_ADDRESS Address;
UINT8 OnLinkPrefixLength;
AddressにはIPアドレスが格納されています。
IPv4(32bits)、IPv6(128bits)のアドレスをそれぞれ
((struct sockaddr_in *)Address.lpSockaddr)->sin_addr
(struct sockaddr_in6 *)Address.lpSockaddr)->sin6_addr
で参照します。
(*)sockaddrはIPv4(sockaddr_in), IPv6(sockaddr_in6)の共通部分を
表す構造体です。
IPv4/IPv6共通で使用できるソケットライブラリでは、アドレス構造体
としてこれを使用します。
EnumInterface.cppは次のようになります。
#include “stdThread.h” #include “AtlBase.h” // unicode(UTF-16) から ShiftJIS に変換用 int main(int argc, char *argv[]) { PIP_ADAPTER_ADDRESSES pAdapterAddresses = NULL, pAA; DWORD dwRet, dwSize; PIP_ADAPTER_UNICAST_ADDRESS pUnicast; sockaddr *pAddr; char szAddr[NI_MAXHOST]; USES_CONVERSION; // unicode(UTF-16) から ShiftJIS に変換 // NULLを指定してネットワークアダプタリストの一覧を格納するために // 必要なバッファサイズを取得 dwRet = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &dwSize); if (dwRet != ERROR_BUFFER_OVERFLOW) goto L_END; // ローカルコンピュータ上のアダプタに関連付けられているアドレスを取得 if ((pAdapterAddresses = (PIP_ADAPTER_ADDRESSES)calloc(dwSize, sizeof(BYTE))) == NULL) goto L_END; dwRet = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAdapterAddresses, &dwSize); if (dwRet != ERROR_SUCCESS) goto L_END; // アダプタリストを先頭から最後までたどる // アダプタリストの先頭のアドレスをセット pAA = pAdapterAddresses; while (pAA != NULL) { // Unicode(UTF-16) から ShiftJIS に変換 fprintf(stderr, “Adapter Name : %s\n”, W2A(pAA->FriendlyName)); // アダプタ名 fprintf(stderr, “IfIndex : %u\n”, pAA->IfIndex); // アダプタインデックス pUnicast = pAA->FirstUnicastAddress; // ユニキャストアドレスリストの先頭アドレスをセット while (pUnicast) { // ユニキャストIPアドレスを列挙 pAddr = pUnicast->Address.lpSockaddr; if (pAddr->sa_family == AF_INET) // IPv4 { inet_ntop(AF_INET, &((struct sockaddr_in *)pAddr)->sin_addr, szAddr, sizeof(szAddr)); fprintf(stderr, ” IPv4: %s/%d\n”, szAddr, pUnicast->OnLinkPrefixLength); } else if (pAddr->sa_family == AF_INET6) // IPv6 { inet_ntop(AF_INET6, &((struct sockaddr_in6 *)pAddr)->sin6_addr, szAddr, sizeof(szAddr)); fprintf(stderr, ” IPv6: %s/%d\n”, szAddr, pUnicast->OnLinkPrefixLength); } pUnicast = pUnicast->Next; // 次のユニキャストアドレスへ } pAA = pAA->Next; } L_END: // バッファの開放 SAFE_FREE(pAdapterAddresses) return(0); }
実行してみましょう。
インターフェースインデックス、インターフェース名、IPアドレスが列挙されます。
C:\projects\EnumInterface\x64\Debug>EnumInterface.exe Adapter Name : イーサネット IfIndex : 6 IPv6: 2001:ce8:132:4332:7985:ee4e:75:1c54/64 IPv6: 2001:ce8:132:4332:3cab:6c05:758:975e/128 IPv6: 2001:ce8:132:4332:854a:c046:5b92:d445/128 IPv6: 2001:ce8:132:4332:d13d:81d2:bbd8:4e4/128 IPv6: 2001:ce8:132:4332:e152:5109:1a14:b590/128 IPv6: 2001:ce8:132:4332:f19b:c60c:ab2e:32c/128 IPv6: 2001:ce8:132:4332:fd68:8417:6924:2fa1/128 IPv6: fe80::7985:ee4e:75:1c54/64 IPv4: 192.168.101.55/24 Adapter Name : ローカル エリア接続* 3 IfIndex : 9 IPv6: fe80::8042:a753:7bb1:fc36/64 IPv4: 169.254.252.54/16 Adapter Name : ローカル エリア接続* 4 IfIndex : 4 IPv6: fe80::b8ee:d2a0:c1e7:e2e3/64 IPv4: 169.254.226.227/16 Adapter Name : Wi-Fi IfIndex : 7 IPv6: fe80::3996:8cb7:30d6:adfe/64 IPv4: 169.254.173.254/16 Adapter Name : Bluetooth ネットワーク接続 IfIndex : 5 IPv6: fe80::c03:39ae:f5c8:e91b/64 IPv4: 169.254.233.27/16 Adapter Name : Loopback Pseudo-Interface 1 IfIndex : 1 IPv6: ::1/128 IPv4: 127.0.0.1/8
【参考】
プロジェクトEnumInterface for Windows(zip)
マルチキャストアドレスも表示するように更新してあります