void UpnpPortMapping::mapPort(const std::string& gateway, const std::string& client, uint16_t port, PortType type, bool enable) { { std::lock_guard<std::mutex> guard(state->stateMutex); for (auto it = state->pendingRequests.begin(); it != state->pendingRequests.end(); it++) { if ((*it)->getHostname() == gateway) { UpnpMappingState::TodoMapping todo; todo.mapping = { gateway, port, type }; todo.client = client; todo.enable = enable; todo.blockingStream = (*it).get(); state->waitingMapping.push_back(todo); return; } } } std::string portStr = std::to_string(port); std::string portType = type == PortType::Tcp ? "TCP" : "UDP"; auto request = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<s:Envelope xmlns:s =\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" "<s:Body>\n" "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\n" "<NewRemoteHost></NewRemoteHost>\n" "<NewExternalPort>" + portStr + "</NewExternalPort>\n" "<NewProtocol>" + portType + "</NewProtocol>\n" "<NewInternalPort>" + portStr + "</NewInternalPort>\n" "<NewInternalClient>" + client + "</NewInternalClient>\n" "<NewEnabled>" + std::string(enable ? "1" : "0") + "</NewEnabled>\n" "<NewPortMappingDescription>mtTorrent UPnP " + portStr + " " + portType + "</NewPortMappingDescription>\n" "<NewLeaseDuration>0</NewLeaseDuration>\n" "</u:AddPortMapping>\n" "</s:Body>\n" "</s:Envelope>\r\n"; auto httpHeader = createUpnpHttpHeader(gateway + ":1900", request.length(), "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"); auto stream = std::make_shared<TcpAsyncStream>(io); state->pendingRequests.push_back(stream); auto streamPtr = stream.get(); auto upnpState = state; stream->onConnectCallback = [streamPtr, httpHeader, request]() { DataBuffer buffer; buffer.assign(httpHeader.begin(), httpHeader.end()); buffer.insert(buffer.end(), request.begin(), request.end()); streamPtr->write(buffer); }; stream->onReceiveCallback = [streamPtr, upnpState, gateway, port, type]() { auto data = streamPtr->getReceivedData(); auto header = HttpHeaderInfo::readFromBuffer(data); if (header.valid && data.size() >= (header.dataStart + header.dataSize)) { streamPtr->consumeData(header.dataStart + header.dataSize); std::lock_guard<std::mutex> guard(upnpState->stateMutex); if (header.success) { upnpState->mappedPorts.push_back({ gateway, port, type }); } } }; stream->onCloseCallback = [streamPtr, upnpState, this](int code) { { std::lock_guard<std::mutex> guard(upnpState->stateMutex); for (auto it = upnpState->pendingRequests.begin(); it != upnpState->pendingRequests.end(); it++) { if ((*it).get() == streamPtr) { upnpState->pendingRequests.erase(it); break; } } } if (upnpState->active) { checkPendingMapping(streamPtr); } }; stream->connect(gateway, 1900); }
void UpnpPortMapping::unmapPort(const std::string& gateway, uint16_t port, PortType type) { std::string portStr = std::to_string(port); std::string portType = type == PortType::Tcp ? "TCP" : "UDP"; auto request = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" "<s:Body>\n" "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\n" "<NewRemoteHost></NewRemoteHost>\n" "<NewProtocol>" + portType + "</NewProtocol>\n" "<NewExternalPort>" + portStr + "</NewExternalPort>\n" "</u:DeletePortMapping>\n" "</s:Body>\n" "</s:Envelope>\r\n"; auto httpHeader = createUpnpHttpHeader(gateway + ":1900", request.length(), "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping"); auto stream = std::make_shared<TcpAsyncStream>(io); auto streamPtr = stream.get(); state->pendingRequests.push_back(stream); auto upnpState = state; stream->onConnectCallback = [streamPtr, upnpState, httpHeader, request]() { DataBuffer buffer; buffer.assign(httpHeader.begin(), httpHeader.end()); buffer.insert(buffer.end(), request.begin(), request.end()); std::lock_guard<std::mutex> guard(upnpState->stateMutex); streamPtr->write(buffer); }; stream->onReceiveCallback = [streamPtr, upnpState, gateway, port, type]() { auto data = streamPtr->getReceivedData(); auto header = HttpHeaderInfo::readFromBuffer(data); if (header.valid && data.size() >= (header.dataStart + header.dataSize)) { streamPtr->consumeData(header.dataStart + header.dataSize); std::lock_guard<std::mutex> guard(upnpState->stateMutex); if (header.success) { for (auto it = upnpState->mappedPorts.begin(); it != upnpState->mappedPorts.end(); it++) { if (it->gateway == gateway && it->port == port && it->type == type) { upnpState->mappedPorts.erase(it); break; } } } } }; stream->onCloseCallback = [streamPtr, upnpState](int code) { std::lock_guard<std::mutex> guard(upnpState->stateMutex); for (auto it = upnpState->pendingRequests.begin(); it != upnpState->pendingRequests.end(); it++) { if ((*it).get() == streamPtr) { upnpState->pendingRequests.erase(it); break; } } }; stream->connect(gateway, 1900); }