| 404 | } |
| 405 | |
| 406 | std::variant<MappingResult, MappingError> PCPRequestPortMap(const PCPMappingNonce &nonce, const CNetAddr &gateway, const CNetAddr &bind, uint16_t port, uint32_t lifetime, CThreadInterrupt& interrupt, int num_tries, std::chrono::milliseconds timeout_per_try) |
| 407 | { |
| 408 | struct sockaddr_storage dest_addr, bind_addr; |
| 409 | socklen_t dest_addrlen = sizeof(struct sockaddr_storage), bind_addrlen = sizeof(struct sockaddr_storage); |
| 410 | |
| 411 | LogDebug(BCLog::NET, "pcp: Requesting port mapping for addr %s port %d from gateway %s\n", bind.ToStringAddr(), port, gateway.ToStringAddr()); |
| 412 | |
| 413 | // Validate addresses, make sure they're the same network family. |
| 414 | if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR; |
| 415 | if (!CService(bind, 0).GetSockAddr((struct sockaddr*)&bind_addr, &bind_addrlen)) return MappingError::NETWORK_ERROR; |
| 416 | if (dest_addr.ss_family != bind_addr.ss_family) return MappingError::NETWORK_ERROR; |
| 417 | |
| 418 | // Create UDP socket (IPv4 or IPv6 based on provided gateway). |
| 419 | auto sock{CreateSock(dest_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)}; |
| 420 | if (!sock) { |
| 421 | LogWarning("pcp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError())); |
| 422 | return MappingError::NETWORK_ERROR; |
| 423 | } |
| 424 | |
| 425 | // Make sure that we send from requested destination address, anything else will be |
| 426 | // rejected by a security-conscious router. |
| 427 | if (sock->Bind((struct sockaddr*)&bind_addr, bind_addrlen) != 0) { |
| 428 | LogWarning("pcp: Could not bind to address: %s\n", NetworkErrorString(WSAGetLastError())); |
| 429 | return MappingError::NETWORK_ERROR; |
| 430 | } |
| 431 | |
| 432 | // Associate UDP socket to gateway. |
| 433 | if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) { |
| 434 | LogWarning("pcp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError())); |
| 435 | return MappingError::NETWORK_ERROR; |
| 436 | } |
| 437 | |
| 438 | // Use getsockname to get the address toward the default gateway (the internal address), |
| 439 | // in case we don't know what address to map |
| 440 | // (this is only needed if bind is INADDR_ANY, but it doesn't hurt as an extra check). |
| 441 | struct sockaddr_storage internal_addr; |
| 442 | socklen_t internal_addrlen = sizeof(struct sockaddr_storage); |
| 443 | if (sock->GetSockName((struct sockaddr*)&internal_addr, &internal_addrlen) != 0) { |
| 444 | LogWarning("pcp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError())); |
| 445 | return MappingError::NETWORK_ERROR; |
| 446 | } |
| 447 | CService internal; |
| 448 | if (!internal.SetSockAddr((struct sockaddr*)&internal_addr, internal_addrlen)) return MappingError::NETWORK_ERROR; |
| 449 | LogDebug(BCLog::NET, "pcp: Internal address after connect: %s\n", internal.ToStringAddr()); |
| 450 | |
| 451 | // Build request packet. Make sure the packet is zeroed so that reserved fields are zero |
| 452 | // as required by the spec (and not potentially leak data). |
| 453 | // Make sure there's space for the request header and MAP specific request data. |
| 454 | std::vector<uint8_t> request(PCP_HDR_SIZE + PCP_MAP_SIZE); |
| 455 | // Fill in request header, See RFC6887 Figure 2. |
| 456 | size_t ofs = 0; |
| 457 | request[ofs + PCP_HDR_VERSION_OFS] = PCP_VERSION; |
| 458 | request[ofs + PCP_HDR_OP_OFS] = PCP_REQUEST | PCP_OP_MAP; |
| 459 | WriteBE32(request.data() + ofs + PCP_HDR_LIFETIME_OFS, lifetime); |
| 460 | if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_REQUEST_HDR_IP_OFS, ADDR_IPV6_SIZE), internal)) return MappingError::NETWORK_ERROR; |
| 461 | |
| 462 | ofs += PCP_HDR_SIZE; |
| 463 | |