2005 年 12 月 30 日 21 時 57 分

C++ からの UPnP Nat Traversal の利用


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


今日はもう一つの壁 UPnPNT を C++ で実装する。
面倒なので、関数化しておく。今のところ汎用じゃないけど。

void RegisterPortMapping(const sockaddr_in &localAddr, sockaddr_in *externalAddr) {

    // 内部ホストの文字列 IP と、自然表現のポート番号を得る。
    char *localIP = inet_ntoa(localAddr.sin_addr);
    u_short localPort = ntohs(localAddr.sin_port);

    // COM の初期化
    CoInitialize(NULL);

    // UPnPNAT オブジェクトの作成。
    IUPnPNAT *nat;
    CoCreateInstance(CLSID_UPnPNAT, NULL, CLSCTX_ALL, IID_IUPnPNAT, (void **)&nat);

    // IStaticPortMappingCollection インタフェースの取得。
    IStaticPortMappingCollection *maps;
    nat->get_StaticPortMappingCollection(&maps);
    // エラー処理をしていないが、利用できない場合 maps に NULL が返る。

    // COM 用に BSTR を割り当てる。

    WCHAR localIPWideString[16];
    MultiByteToWideChar(CP_ACP, 0, localIP, -1, localIPWideString, 16);
    BSTR localIPBstr =
SysAllocString(localIPWideString);
    BSTR proto = SysAllocString(L"TCP");
    BSTR description = SysAllocString(L"FTP Proxy");

    // 空きポートを自動的に割り当てることはできないらしいので、
    // 片っ端に適当に空きポートを探してマッピング。
    IStaticPortMapping *map;
    long externalPort = 1024;
    HRESULT result = S_OK;
    do {
        // choice between 1024 and 4095

        externalPort = 1024 + (long)((double)rand() * 3072 / (RAND_MAX + 1));
        result = maps->Add(externalPort, proto, localPort, localIPBstr,
          VARIANT_TRUE, description, &map);
    } while (SUCCEEDED(result));

    // BSTR の解放。

    SysFreeString(description);
    SysFreeString(proto);
    SysFreeString(localIPBstr);

    // 外部 IP の文字列表現を得る。
    BSTR externalIPBstr;
    char externalIPString[16];
    map->get_ExternalIPAddress(&externalIPBstr);
    WideCharToMultiByte(CP_ACP, 0, externalIPBstr, -1, externalIPString, 16, NULL, NULL);
    SysFreeString(externalIPW);

    externalAddr->sin_family = AF_INET;
    externalAddr->sin_port = htons(externalPort);
    externalAddr->sin_addr.s_addr = inet_addr(externalIPString);

    // オブジェクト解放しまくり。
    map->Release();
    maps->Release();
    nat->Release();

    // COM の後処理
    CoUninitialize();
}

エラー処理などがないのは置いといても、変換が多くてコードが汚い汚い。
sockaddr_in や COM のラッパとか作りたいものがたくさんある。
ま、そのあたりは後で実装しようか。今日はテストできてないなぁ。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.