Attempts to expose a local UDP port through the router.
110 {
112 private_port = 0;
113 status_message = "Invalid port; automatic router mapping skipped.";
114 return;
115 }
116 public_port = private_port;
117 const std::string port = std::to_string(private_port);
118 std::string upnp_failure = "UPnP support was not built";
119 std::string natpmp_failure = "NAT-PMP support was not built";
120
121#if defined(ASTEROIDS_NET_HAS_MINIUPNPC)
122 int error = 0;
123 UPNPDev *devices = upnpDiscover(1500, nullptr, nullptr, 0, 0, 2, &error);
124 UPNPUrls urls{};
125 IGDdatas data{};
126 char local_address[64]{};
127 char external_address[64]{};
128 const int igd_result = devices == nullptr
129 ? 0
130 : get_valid_igd(devices, urls, data, local_address, sizeof(local_address), external_address,
131 sizeof(external_address));
132 if (igd_result > 0) {
133 const int result = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(),
134 local_address, "asteroids-net", "UDP", nullptr,
135 std::to_string(MAPPING_LIFETIME_SECONDS).c_str());
136 if (result == UPNPCOMMAND_SUCCESS) {
137 if (external_address[0] == '\0')
138 UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, external_address);
139 method = Method::Upnp;
140 status_message = std::string("UPnP mapped UDP ") + port;
141 if (external_address[0] != '\0')
142 status_message += std::string(" at ") + external_address + ":" + port;
143 status_message += "; waiting for players...";
144 FreeUPNPUrls(&urls);
145 freeUPNPDevlist(devices);
146 return;
147 }
148 upnp_failure = std::string("UPnP mapping failed: ") + strupnperror(result);
149 FreeUPNPUrls(&urls);
150 } else if (devices == nullptr) {
151 upnp_failure = "no UPnP devices responded";
152 } else {
153 upnp_failure = "no valid UPnP internet gateway";
154 }
155 if (devices != nullptr)
156 freeUPNPDevlist(devices);
157#endif
158
159#if defined(ASTEROIDS_NET_HAS_NATPMP)
160 natpmp_t natpmp{};
161 const int init_result = initnatpmp(&natpmp, 0, 0);
162 if (init_result >= 0) {
163 const int sent = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_UDP, private_port, public_port, MAPPING_LIFETIME_SECONDS);
164 natpmpresp_t response{};
165 const int response_result = sent < 0 ? sent : wait_for_natpmp(natpmp, response);
166 if (response_result >= 0) {
167 public_port = response.pnu.newportmapping.mappedpublicport;
168 method = Method::NatPmp;
169 status_message = "NAT-PMP mapped UDP " + std::to_string(public_port) + "; waiting for players...";
170 closenatpmp(&natpmp);
171 return;
172 }
173 natpmp_failure = natpmp_error(response_result);
174 closenatpmp(&natpmp);
175 } else {
176 natpmp_failure = natpmp_error(init_result);
177 }
178#endif
179
180 private_port = 0;
181 public_port = 0;
182 status_message = upnp_failure + "; " + natpmp_failure + ". Forward UDP " + port + " manually.";
183 }
bool parse_port(const std::string &text, std::uint16_t &port)