| 280 | } |
| 281 | |
| 282 | std::variant<MappingResult, MappingError> NATPMPRequestPortMap(const CNetAddr &gateway, uint16_t port, uint32_t lifetime, CThreadInterrupt& interrupt, int num_tries, std::chrono::milliseconds timeout_per_try) |
| 283 | { |
| 284 | struct sockaddr_storage dest_addr; |
| 285 | socklen_t dest_addrlen = sizeof(struct sockaddr_storage); |
| 286 | |
| 287 | LogDebug(BCLog::NET, "natpmp: Requesting port mapping port %d from gateway %s\n", port, gateway.ToStringAddr()); |
| 288 | |
| 289 | // Validate gateway, make sure it's IPv4. NAT-PMP does not support IPv6. |
| 290 | if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR; |
| 291 | if (dest_addr.ss_family != AF_INET) return MappingError::NETWORK_ERROR; |
| 292 | |
| 293 | // Create IPv4 UDP socket |
| 294 | auto sock{CreateSock(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; |
| 295 | if (!sock) { |
| 296 | LogWarning("natpmp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError())); |
| 297 | return MappingError::NETWORK_ERROR; |
| 298 | } |
| 299 | |
| 300 | // Associate UDP socket to gateway. |
| 301 | if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) { |
| 302 | LogWarning("natpmp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError())); |
| 303 | return MappingError::NETWORK_ERROR; |
| 304 | } |
| 305 | |
| 306 | // Use getsockname to get the address toward the default gateway (the internal address). |
| 307 | struct sockaddr_in internal; |
| 308 | socklen_t internal_addrlen = sizeof(struct sockaddr_in); |
| 309 | if (sock->GetSockName((struct sockaddr*)&internal, &internal_addrlen) != 0) { |
| 310 | LogWarning("natpmp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError())); |
| 311 | return MappingError::NETWORK_ERROR; |
| 312 | } |
| 313 | |
| 314 | // Request external IP address (RFC6886 section 3.2). |
| 315 | std::vector<uint8_t> request(NATPMP_GETEXTERNAL_REQUEST_SIZE); |
| 316 | request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION; |
| 317 | request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_GETEXTERNAL; |
| 318 | |
| 319 | auto recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try, |
| 320 | [&](const std::span<const uint8_t> response) -> bool { |
| 321 | if (response.size() < NATPMP_GETEXTERNAL_RESPONSE_SIZE) { |
| 322 | LogWarning("natpmp: Response too small\n"); |
| 323 | return false; // Wasn't response to what we expected, try receiving next packet. |
| 324 | } |
| 325 | if (response[NATPMP_HDR_VERSION_OFS] != NATPMP_VERSION || response[NATPMP_HDR_OP_OFS] != (NATPMP_RESPONSE | NATPMP_OP_GETEXTERNAL)) { |
| 326 | LogWarning("natpmp: Response to wrong command\n"); |
| 327 | return false; // Wasn't response to what we expected, try receiving next packet. |
| 328 | } |
| 329 | return true; |
| 330 | }, |
| 331 | interrupt); |
| 332 | |
| 333 | struct in_addr external_addr; |
| 334 | if (recv_res) { |
| 335 | const std::span<const uint8_t> response = *recv_res; |
| 336 | |
| 337 | Assume(response.size() >= NATPMP_GETEXTERNAL_RESPONSE_SIZE); |
| 338 | uint16_t result_code = ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS); |
| 339 | if (result_code != NATPMP_RESULT_SUCCESS) { |