#include "stdafx.h" #include "UPnPPortMapper.h" #ifdef _WIN32 #include #include #include bool UPnPPortMapper::AddNATPortMapping(uint16_t internalPort, uint16_t externalPort, IPProtocol protocol) { bool result = false; CoInitializeEx(nullptr, COINIT_MULTITHREADED); IUPnPNAT *nat = nullptr; HRESULT hResult = CoCreateInstance(__uuidof(UPnPNAT), nullptr, CLSCTX_ALL, __uuidof(IUPnPNAT), (void**)&nat); BSTR proto = SysAllocString((protocol == IPProtocol::TCP) ? L"TCP" : L"UDP"); if(SUCCEEDED(hResult) && nat) { IStaticPortMappingCollection *spmc = nullptr; hResult = nat->get_StaticPortMappingCollection(&spmc); if(SUCCEEDED(hResult) && spmc) { IStaticPortMapping *spm = nullptr; hResult = spmc->get_Item(externalPort, proto, &spm); if(spm != nullptr) { //An identical mapping already exists, remove it if(RemoveNATPortMapping(externalPort, protocol)) { std::cout << "Removed existing UPnP mapping." << std::endl; spm->Release(); spm = nullptr; } } if(!SUCCEEDED(hResult) || spm == nullptr) { std::cout << "Attempting to automatically forward port via UPnP..." << std::endl; vector localIPs = GetLocalIPs(); BSTR desc = SysAllocString(L"Mesen NetPlay"); spm = nullptr; for(size_t i = 0, len = localIPs.size(); i < len; i++) { BSTR clientStr = SysAllocString(localIPs[i].c_str()); hResult = spmc->Add(externalPort, proto, internalPort, clientStr, true, desc, &spm); SysFreeString(clientStr); SysFreeString(desc); if(SUCCEEDED(hResult) && spm) { //Successfully added a new port mapping std::cout << std::dec << "Forwarded port " << externalPort << " to IP " << utf8::utf8::encode(localIPs[i]) << std::endl; result = true; } else { std::cout << "Unable to add UPnP port mapping. IP: " << utf8::utf8::encode(localIPs[i]) << " HRESULT: 0x" << std::hex << hResult << std::endl; } if(spm) { spm->Release(); } } } else { std::cout << "Unable to add UPnP port mapping." << std::endl; } spmc->Release(); } nat->Release(); } SysFreeString(proto); CoUninitialize(); return result; } bool UPnPPortMapper::RemoveNATPortMapping(uint16_t externalPort, IPProtocol protocol) { IUPnPNAT *nat = nullptr; IStaticPortMappingCollection *spmc; bool result = false; CoInitializeEx(nullptr, COINIT_MULTITHREADED); HRESULT hResult = ::CoCreateInstance(__uuidof(UPnPNAT), nullptr, CLSCTX_ALL, __uuidof(IUPnPNAT), (void**)&nat); BSTR proto = SysAllocString((protocol == IPProtocol::TCP) ? L"TCP" : L"UDP"); if(SUCCEEDED(hResult) && nat) { hResult = nat->get_StaticPortMappingCollection(&spmc); if(SUCCEEDED(hResult) && spmc) { spmc->Remove(externalPort, proto); spmc->Release(); result = true; } nat->Release(); } SysFreeString(proto); CoUninitialize(); return result; } vector UPnPPortMapper::GetLocalIPs() { vector localIPs; ADDRINFOW *result = nullptr; ADDRINFOW *current = nullptr; ADDRINFOW hints; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; wchar_t hostName[255]; DWORD hostSize = 255; GetComputerName(hostName, &hostSize); if(GetAddrInfoW(hostName, nullptr, &hints, &result) == 0) { current = result; while(current != nullptr) { wchar_t ipAddr[255]; DWORD ipSize = 255; if(WSAAddressToString(current->ai_addr, (DWORD)current->ai_addrlen, nullptr, ipAddr, &ipSize) == 0) { if(std::find(localIPs.begin(), localIPs.end(), ipAddr) == localIPs.end()) { localIPs.push_back(ipAddr); } } current = current->ai_next; } FreeAddrInfoW(result); } return localIPs; } #else bool UPnPPortMapper::AddNATPortMapping(uint16_t internalPort, uint16_t externalPort, IPProtocol protocol) { return false; } bool UPnPPortMapper::RemoveNATPortMapping(uint16_t externalPort, IPProtocol protocol) { return false; } vector UPnPPortMapper::GetLocalIPs() { return vector(); } #endif