blob: cf45b1228ae44f05687fbea76d5c6a493c2c3821 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
Adrien Béraudcb753622023-07-17 22:32:49 -040015 * along with this program. If not, see <https://www.gnu.org/licenses/>.
Adrien Béraud612b55b2023-05-29 10:42:04 -040016 */
Adrien Béraud612b55b2023-05-29 10:42:04 -040017#include "nat_pmp.h"
Sébastien Blin4cf587e2024-03-08 16:00:18 -050018#include <poll.h>
Adrien Béraud612b55b2023-05-29 10:42:04 -040019
20#if HAVE_LIBNATPMP
21
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040022namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040023namespace upnp {
24
Adrien Béraud370257c2023-08-15 20:53:09 -040025NatPmp::NatPmp(const std::shared_ptr<asio::io_context>& ctx, const std::shared_ptr<dht::log::Logger>& logger)
26 : UPnPProtocol(logger), ioContext(ctx), searchForIgdTimer_(*ctx)
Adrien Béraud612b55b2023-05-29 10:42:04 -040027{
Morteza Namvar5f639522023-07-04 17:08:58 -040028 // JAMI_DBG("NAT-PMP: Instance [%p] created", this);
Adrien Béraud370257c2023-08-15 20:53:09 -040029 ioContext->dispatch([this] {
Adrien Béraud612b55b2023-05-29 10:42:04 -040030 igd_ = std::make_shared<PMPIGD>();
31 });
32}
33
34NatPmp::~NatPmp()
35{
Morteza Namvar5f639522023-07-04 17:08:58 -040036 // JAMI_DBG("NAT-PMP: Instance [%p] destroyed", this);
Adrien Béraud612b55b2023-05-29 10:42:04 -040037}
38
39void
40NatPmp::initNatPmp()
41{
Adrien Béraud612b55b2023-05-29 10:42:04 -040042 initialized_ = false;
43
44 {
Adrien Béraud024c46f2024-03-02 23:53:18 -050045 std::lock_guard lock(natpmpMutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -040046 hostAddress_ = ip_utils::getLocalAddr(AF_INET);
47 }
48
49 // Local address must be valid.
50 if (not getHostAddress() or getHostAddress().isLoopback()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040051 if (logger_) logger_->warn("NAT-PMP: Does not have a valid local address!");
Adrien Béraud612b55b2023-05-29 10:42:04 -040052 return;
53 }
54
55 assert(igd_);
56 if (igd_->isValid()) {
57 igd_->setValid(false);
58 processIgdUpdate(UpnpIgdEvent::REMOVED);
59 }
60
61 igd_->setLocalIp(IpAddr());
62 igd_->setPublicIp(IpAddr());
63 igd_->setUID("");
64
Adrien Béraud17765bd2023-08-22 21:02:38 -040065 if (logger_) logger_->debug("NAT-PMP: Trying to initialize IGD");
Adrien Béraud612b55b2023-05-29 10:42:04 -040066
67 int err = initnatpmp(&natpmpHdl_, 0, 0);
68
69 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040070 if (logger_) logger_->warn("NAT-PMP: Initializing IGD using default gateway failed!");
Adrien Béraud612b55b2023-05-29 10:42:04 -040071 const auto& localGw = ip_utils::getLocalGateway();
72 if (not localGw) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040073 if (logger_) logger_->warn("NAT-PMP: Couldn't find valid gateway on local host");
Adrien Béraud612b55b2023-05-29 10:42:04 -040074 err = NATPMP_ERR_CANNOTGETGATEWAY;
75 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -040076 if (logger_) logger_->warn("NAT-PMP: Trying to initialize using detected gateway {}",
77 localGw.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -040078 struct in_addr inaddr;
79 inet_pton(AF_INET, localGw.toString().c_str(), &inaddr);
80 err = initnatpmp(&natpmpHdl_, 1, inaddr.s_addr);
81 }
82 }
83
84 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040085 if (logger_) logger_->error("NAT-PMP: Can't initialize libnatpmp -> {}", getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -040086 return;
87 }
88
89 char addrbuf[INET_ADDRSTRLEN];
90 inet_ntop(AF_INET, &natpmpHdl_.gateway, addrbuf, sizeof(addrbuf));
91 IpAddr igdAddr(addrbuf);
Adrien Béraud17765bd2023-08-22 21:02:38 -040092 if (logger_) logger_->debug("NAT-PMP: Initialized on gateway {}", igdAddr.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -040093
94 // Set the local (gateway) address.
95 igd_->setLocalIp(igdAddr);
96 // NAT-PMP protocol does not have UID, but we will set generic
97 // one debugging purposes.
98 igd_->setUID("NAT-PMP Gateway");
99
100 // Search and set the public address.
101 getIgdPublicAddress();
102
103 // Update and notify.
104 if (igd_->isValid()) {
105 initialized_ = true;
106 processIgdUpdate(UpnpIgdEvent::ADDED);
107 };
108}
109
110void
111NatPmp::setObserver(UpnpMappingObserver* obs)
112{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400113 observer_ = obs;
114}
115
116void
117NatPmp::terminate(std::condition_variable& cv)
118{
Adrien Béraud7a82bee2023-08-30 10:26:45 -0400119 if (logger_) logger_->debug("NAT-PMP: Terminate instance {}", fmt::ptr(this));
120
Adrien Béraud612b55b2023-05-29 10:42:04 -0400121 initialized_ = false;
122 observer_ = nullptr;
123
Adrien Béraud024c46f2024-03-02 23:53:18 -0500124 std::lock_guard lock(natpmpMutex_);
Adrien Béraud7a82bee2023-08-30 10:26:45 -0400125 shutdownComplete_ = true;
126 cv.notify_one();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400127}
128
129void
130NatPmp::terminate()
131{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400132 std::condition_variable cv {};
133
Adrien Béraud370257c2023-08-15 20:53:09 -0400134 ioContext->dispatch([&] {
135 terminate(cv);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400136 });
137
Adrien Béraud024c46f2024-03-02 23:53:18 -0500138 std::unique_lock lk(natpmpMutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400139 if (cv.wait_for(lk, std::chrono::seconds(10), [this] { return shutdownComplete_; })) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400140 if (logger_) logger_->debug("NAT-PMP: Shutdown completed");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400141 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400142 if (logger_) logger_->error("NAT-PMP: Shutdown timed-out");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400143 }
144}
145
146const IpAddr
147NatPmp::getHostAddress() const
148{
Adrien Béraud024c46f2024-03-02 23:53:18 -0500149 std::lock_guard lock(natpmpMutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400150 return hostAddress_;
151}
152
153void
154NatPmp::clearIgds()
155{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400156 bool do_close = false;
157
158 if (igd_) {
159 if (igd_->isValid()) {
160 do_close = true;
161 }
162 igd_->setValid(false);
163 }
164
165 initialized_ = false;
Adrien Béraud370257c2023-08-15 20:53:09 -0400166 searchForIgdTimer_.cancel();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400167
168 igdSearchCounter_ = 0;
169
170 if (do_close) {
171 closenatpmp(&natpmpHdl_);
172 memset(&natpmpHdl_, 0, sizeof(natpmpHdl_));
173 }
174}
175
176void
177NatPmp::searchForIgd()
178{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400179 if (not initialized_) {
180 initNatPmp();
181 }
182
183 // Schedule a retry in case init failed.
184 if (not initialized_) {
185 if (igdSearchCounter_++ < MAX_RESTART_SEARCH_RETRIES) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400186 if (logger_) logger_->debug("NAT-PMP: Start search for IGDs. Attempt {}", igdSearchCounter_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400187
188 // Cancel the current timer (if any) and re-schedule.
Adrien Béraud370257c2023-08-15 20:53:09 -0400189 searchForIgdTimer_.expires_after(NATPMP_SEARCH_RETRY_UNIT * igdSearchCounter_);
Sébastien Blind1e018c2023-09-01 09:15:12 -0400190 searchForIgdTimer_.async_wait([w=weak()](const asio::error_code& ec) {
191 if (!ec) {
192 if (auto shared = w.lock())
193 shared->searchForIgd();
194 }
Adrien Béraud370257c2023-08-15 20:53:09 -0400195 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400196 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400197 if (logger_) logger_->warn("NAT-PMP: Setup failed after {} trials. NAT-PMP will be disabled!",
198 MAX_RESTART_SEARCH_RETRIES);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400199 }
200 }
201}
202
203std::list<std::shared_ptr<IGD>>
204NatPmp::getIgdList() const
205{
Adrien Béraud024c46f2024-03-02 23:53:18 -0500206 std::lock_guard lock(natpmpMutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400207 std::list<std::shared_ptr<IGD>> igdList;
208 if (igd_->isValid())
209 igdList.emplace_back(igd_);
210 return igdList;
211}
212
213bool
214NatPmp::isReady() const
215{
216 if (observer_ == nullptr) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400217 if (logger_) logger_->error("NAT-PMP: the observer is not set!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400218 return false;
219 }
220
221 // Must at least have a valid local address.
222 if (not getHostAddress() or getHostAddress().isLoopback())
223 return false;
224
225 return igd_ and igd_->isValid();
226}
227
228void
229NatPmp::incrementErrorsCounter(const std::shared_ptr<IGD>& igdIn)
230{
231 if (not validIgdInstance(igdIn)) {
232 return;
233 }
234
235 if (not igd_->isValid()) {
236 // Already invalid. Nothing to do.
237 return;
238 }
239
240 if (not igd_->incrementErrorsCounter()) {
241 // Disable this IGD.
242 igd_->setValid(false);
243 // Notify the listener.
Adrien Béraud17765bd2023-08-22 21:02:38 -0400244 if (logger_) logger_->warn("NAT-PMP: No more valid IGD!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400245
246 processIgdUpdate(UpnpIgdEvent::INVALID_STATE);
247 }
248}
249
250void
251NatPmp::requestMappingAdd(const Mapping& mapping)
252{
253 // Process on nat-pmp thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400254 /*if (not isValidThread()) {
255 ioContext->post([w = weak(), mapping] {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400256 if (auto pmpThis = w.lock()) {
257 pmpThis->requestMappingAdd(mapping);
258 }
259 });
260 return;
Adrien Béraud370257c2023-08-15 20:53:09 -0400261 }*/
Adrien Béraud612b55b2023-05-29 10:42:04 -0400262
263 Mapping map(mapping);
264 assert(map.getIgd());
265 auto err = addPortMapping(map);
266 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400267 if (logger_) logger_->warn("NAT-PMP: Request for mapping {} on {} failed with error {:d}: {}",
268 map.toString(),
269 igd_->toString(),
270 err,
271 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400272
273 if (isErrorFatal(err)) {
274 // Fatal error, increment the counter.
275 incrementErrorsCounter(igd_);
276 }
277 // Notify the listener.
278 processMappingRequestFailed(std::move(map));
279 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400280 if (logger_) logger_->debug("NAT-PMP: Request for mapping {:s} on {:s} succeeded",
281 map.toString(),
282 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400283 // Notify the listener.
284 processMappingAdded(std::move(map));
285 }
286}
287
288void
289NatPmp::requestMappingRenew(const Mapping& mapping)
290{
291 // Process on nat-pmp thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400292 /*if (not isValidThread()) {
293 ioContext->post([w = weak(), mapping] {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400294 if (auto pmpThis = w.lock()) {
295 pmpThis->requestMappingRenew(mapping);
296 }
297 });
298 return;
Adrien Béraud370257c2023-08-15 20:53:09 -0400299 }*/
Adrien Béraud612b55b2023-05-29 10:42:04 -0400300
301 Mapping map(mapping);
302 auto err = addPortMapping(map);
303 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400304 if (logger_) logger_->warn("NAT-PMP: Renewal request for mapping {} on {} failed with error {:d}: {}",
305 map.toString().c_str(),
306 igd_->toString().c_str(),
307 err,
308 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400309 // Notify the listener.
310 processMappingRequestFailed(std::move(map));
311
312 if (isErrorFatal(err)) {
313 // Fatal error, increment the counter.
314 incrementErrorsCounter(igd_);
315 }
316 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400317 if (logger_) logger_->debug("NAT-PMP: Renewal request for mapping {} on {} succeeded",
318 map.toString().c_str(),
319 igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400320 // Notify the listener.
321 processMappingRenewed(map);
322 }
323}
324
325int
326NatPmp::readResponse(natpmp_t& handle, natpmpresp_t& response)
327{
328 int err = 0;
329 unsigned readRetriesCounter = 0;
330
331 while (true) {
332 if (readRetriesCounter++ > MAX_READ_RETRIES) {
333 err = NATPMP_ERR_SOCKETERROR;
334 break;
335 }
336
Sébastien Blin4cf587e2024-03-08 16:00:18 -0500337 struct pollfd fds;
338 fds.fd = handle.s;
339 fds.events = POLLIN;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400340 struct timeval timeout;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400341 getnatpmprequesttimeout(&handle, &timeout);
Sébastien Blin4cf587e2024-03-08 16:00:18 -0500342 uint64_t millis = (timeout.tv_sec * (uint64_t)1000) + (timeout.tv_usec / 1000);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400343 // Wait for data.
Sébastien Blin4cf587e2024-03-08 16:00:18 -0500344 if (poll(&fds, 1, millis) == -1) {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400345 err = NATPMP_ERR_SOCKETERROR;
346 break;
347 }
348
349 // Read the data.
350 err = readnatpmpresponseorretry(&handle, &response);
351
352 if (err == NATPMP_TRYAGAIN) {
353 std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT_BEFORE_READ_RETRY));
354 } else {
355 break;
356 }
357 }
358
359 return err;
360}
361
362int
363NatPmp::sendMappingRequest(const Mapping& mapping, uint32_t& lifetime)
364{
Adrien Béraud370257c2023-08-15 20:53:09 -0400365 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400366
367 int err = sendnewportmappingrequest(&natpmpHdl_,
368 mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP
369 : NATPMP_PROTOCOL_TCP,
370 mapping.getInternalPort(),
371 mapping.getExternalPort(),
372 lifetime);
373
374 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400375 if (logger_) logger_->error("NAT-PMP: Send mapping request failed with error {} {:d}",
376 getNatPmpErrorStr(err),
377 errno);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400378 return err;
379 }
380
381 unsigned readRetriesCounter = 0;
382
383 while (readRetriesCounter++ < MAX_READ_RETRIES) {
384 // Read the response
385 natpmpresp_t response;
386 err = readResponse(natpmpHdl_, response);
387
388 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400389 if (logger_) logger_->warn("NAT-PMP: Read response on IGD {} failed with error {}",
390 igd_->toString(),
391 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400392 } else if (response.type != NATPMP_RESPTYPE_TCPPORTMAPPING
393 and response.type != NATPMP_RESPTYPE_UDPPORTMAPPING) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400394 if (logger_) logger_->error("NAT-PMP: Unexpected response type ({:d}) for mapping {} from IGD {}.",
395 response.type,
396 mapping.toString(),
397 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400398 // Try to read again.
399 continue;
400 }
401
402 lifetime = response.pnu.newportmapping.lifetime;
403 // Done.
404 break;
405 }
406
407 return err;
408}
409
410int
411NatPmp::addPortMapping(Mapping& mapping)
412{
413 auto const& igdIn = mapping.getIgd();
414 assert(igdIn);
415 assert(igdIn->getProtocol() == NatProtocolType::NAT_PMP);
416
417 if (not igdIn->isValid() or not validIgdInstance(igdIn)) {
418 mapping.setState(MappingState::FAILED);
419 return NATPMP_ERR_INVALIDARGS;
420 }
421
422 mapping.setInternalAddress(getHostAddress().toString());
423
424 uint32_t lifetime = MAPPING_ALLOCATION_LIFETIME;
425 int err = sendMappingRequest(mapping, lifetime);
426
427 if (err < 0) {
428 mapping.setState(MappingState::FAILED);
429 return err;
430 }
431
432 // Set the renewal time and update.
433 mapping.setRenewalTime(sys_clock::now() + std::chrono::seconds(lifetime * 4 / 5));
434 mapping.setState(MappingState::OPEN);
435
436 return 0;
437}
438
439void
440NatPmp::requestMappingRemove(const Mapping& mapping)
441{
Adrien Béraud370257c2023-08-15 20:53:09 -0400442 ioContext->dispatch([w = weak(), mapping] {
443 if (auto pmpThis = w.lock()) {
444 Mapping map {mapping};
445 pmpThis->removePortMapping(map);
446 }
447 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400448}
449
450void
451NatPmp::removePortMapping(Mapping& mapping)
452{
453 auto igdIn = mapping.getIgd();
454 assert(igdIn);
455 if (not igdIn->isValid()) {
456 return;
457 }
458
459 if (not validIgdInstance(igdIn)) {
460 return;
461 }
462
463 Mapping mapToRemove(mapping);
464
465 uint32_t lifetime = 0;
466 int err = sendMappingRequest(mapping, lifetime);
467
468 if (err < 0) {
469 // Nothing to do if the request fails, just log the error.
Adrien Béraud17765bd2023-08-22 21:02:38 -0400470 if (logger_) logger_->warn("NAT-PMP: Send remove request failed with error {}. Ignoring",
471 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400472 }
473
474 // Update and notify the listener.
475 mapToRemove.setState(MappingState::FAILED);
476 processMappingRemoved(std::move(mapToRemove));
477}
478
479void
480NatPmp::getIgdPublicAddress()
481{
Adrien Béraud370257c2023-08-15 20:53:09 -0400482 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400483
484 // Set the public address for this IGD if it does not
485 // have one already.
486 if (igd_->getPublicIp()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400487 if (logger_) logger_->warn("NAT-PMP: IGD {} already have a public address ({})",
488 igd_->toString(),
489 igd_->getPublicIp().toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400490 return;
491 }
492 assert(igd_->getProtocol() == NatProtocolType::NAT_PMP);
493
494 int err = sendpublicaddressrequest(&natpmpHdl_);
495
496 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400497 if (logger_) logger_->error("NAT-PMP: send public address request on IGD {} failed with error: {}",
498 igd_->toString(),
499 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400500
501 if (isErrorFatal(err)) {
502 // Fatal error, increment the counter.
503 incrementErrorsCounter(igd_);
504 }
505 return;
506 }
507
508 natpmpresp_t response;
509 err = readResponse(natpmpHdl_, response);
510
511 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400512 if (logger_) logger_->warn("NAT-PMP: Read response on IGD {} failed - {}",
513 igd_->toString(),
514 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400515 return;
516 }
517
518 if (response.type != NATPMP_RESPTYPE_PUBLICADDRESS) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400519 if (logger_) logger_->error("NAT-PMP: Unexpected response type ({:d}) for public address request from IGD {}.",
520 response.type,
521 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400522 return;
523 }
524
525 IpAddr publicAddr(response.pnu.publicaddress.addr);
526
527 if (not publicAddr) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400528 if (logger_) logger_->error("NAT-PMP: IGD {} returned an invalid public address {}",
529 igd_->toString(),
530 publicAddr.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400531 }
532
533 // Update.
534 igd_->setPublicIp(publicAddr);
535 igd_->setValid(true);
536
Adrien Béraud17765bd2023-08-22 21:02:38 -0400537 if (logger_) logger_->debug("NAT-PMP: Setting IGD {} public address to {}",
538 igd_->toString(),
539 igd_->getPublicIp().toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400540}
541
542void
543NatPmp::removeAllMappings()
544{
Adrien Béraud370257c2023-08-15 20:53:09 -0400545 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400546
Adrien Béraud17765bd2023-08-22 21:02:38 -0400547 if (logger_) logger_->warn("NAT-PMP: Send request to close all existing mappings to IGD {}",
548 igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400549
550 int err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_TCP, 0, 0, 0);
551 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400552 if (logger_) logger_->warn("NAT-PMP: Send close all TCP mappings request failed with error {}",
553 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400554 }
555 err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_UDP, 0, 0, 0);
556 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400557 if (logger_) logger_->warn("NAT-PMP: Send close all UDP mappings request failed with error {}",
558 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400559 }
560}
561
562const char*
563NatPmp::getNatPmpErrorStr(int errorCode) const
564{
565#ifdef ENABLE_STRNATPMPERR
566 return strnatpmperr(errorCode);
567#else
568 switch (errorCode) {
569 case NATPMP_ERR_INVALIDARGS:
570 return "INVALIDARGS";
571 break;
572 case NATPMP_ERR_SOCKETERROR:
573 return "SOCKETERROR";
574 break;
575 case NATPMP_ERR_CANNOTGETGATEWAY:
576 return "CANNOTGETGATEWAY";
577 break;
578 case NATPMP_ERR_CLOSEERR:
579 return "CLOSEERR";
580 break;
581 case NATPMP_ERR_RECVFROM:
582 return "RECVFROM";
583 break;
584 case NATPMP_ERR_NOPENDINGREQ:
585 return "NOPENDINGREQ";
586 break;
587 case NATPMP_ERR_NOGATEWAYSUPPORT:
588 return "NOGATEWAYSUPPORT";
589 break;
590 case NATPMP_ERR_CONNECTERR:
591 return "CONNECTERR";
592 break;
593 case NATPMP_ERR_WRONGPACKETSOURCE:
594 return "WRONGPACKETSOURCE";
595 break;
596 case NATPMP_ERR_SENDERR:
597 return "SENDERR";
598 break;
599 case NATPMP_ERR_FCNTLERROR:
600 return "FCNTLERROR";
601 break;
602 case NATPMP_ERR_GETTIMEOFDAYERR:
603 return "GETTIMEOFDAYERR";
604 break;
605 case NATPMP_ERR_UNSUPPORTEDVERSION:
606 return "UNSUPPORTEDVERSION";
607 break;
608 case NATPMP_ERR_UNSUPPORTEDOPCODE:
609 return "UNSUPPORTEDOPCODE";
610 break;
611 case NATPMP_ERR_UNDEFINEDERROR:
612 return "UNDEFINEDERROR";
613 break;
614 case NATPMP_ERR_NOTAUTHORIZED:
615 return "NOTAUTHORIZED";
616 break;
617 case NATPMP_ERR_NETWORKFAILURE:
618 return "NETWORKFAILURE";
619 break;
620 case NATPMP_ERR_OUTOFRESOURCES:
621 return "OUTOFRESOURCES";
622 break;
623 case NATPMP_TRYAGAIN:
624 return "TRYAGAIN";
625 break;
626 default:
627 return "UNKNOWNERR";
628 break;
629 }
630#endif
631}
632
633bool
634NatPmp::isErrorFatal(int error)
635{
636 switch (error) {
637 case NATPMP_ERR_INVALIDARGS:
638 case NATPMP_ERR_SOCKETERROR:
639 case NATPMP_ERR_CANNOTGETGATEWAY:
640 case NATPMP_ERR_CLOSEERR:
641 case NATPMP_ERR_RECVFROM:
642 case NATPMP_ERR_NOGATEWAYSUPPORT:
643 case NATPMP_ERR_CONNECTERR:
644 case NATPMP_ERR_SENDERR:
645 case NATPMP_ERR_UNDEFINEDERROR:
646 case NATPMP_ERR_UNSUPPORTEDVERSION:
647 case NATPMP_ERR_UNSUPPORTEDOPCODE:
648 case NATPMP_ERR_NOTAUTHORIZED:
649 case NATPMP_ERR_NETWORKFAILURE:
650 case NATPMP_ERR_OUTOFRESOURCES:
651 return true;
652 default:
653 return false;
654 }
655}
656
657bool
658NatPmp::validIgdInstance(const std::shared_ptr<IGD>& igdIn)
659{
660 if (igd_.get() != igdIn.get()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400661 if (logger_) logger_->error("NAT-PMP: IGD ({}) does not match local instance ({})",
662 igdIn->toString(),
663 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400664 return false;
665 }
666
667 return true;
668}
669
670void
671NatPmp::processIgdUpdate(UpnpIgdEvent event)
672{
673 if (igd_->isValid()) {
674 // Remove all current mappings if any.
675 removeAllMappings();
676 }
677
678 if (observer_ == nullptr)
679 return;
680 // Process the response on the context thread.
Sébastien Blinf6baf4b2024-01-03 15:51:36 -0500681 ioContext->post([w = weak(), event] {
682 if (auto shared = w.lock()) {
683 if (!shared->shutdownComplete_) {
684 shared->observer_->onIgdUpdated(shared->igd_, event);
685 }
686 }
687 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400688}
689
690void
691NatPmp::processMappingAdded(const Mapping& map)
692{
693 if (observer_ == nullptr)
694 return;
695
696 // Process the response on the context thread.
Sébastien Blinf6baf4b2024-01-03 15:51:36 -0500697 ioContext->post([w=weak(), map] {
698 if (auto shared = w.lock()) {
699 if (!shared->shutdownComplete_) {
700 shared->observer_->onMappingAdded(shared->igd_, map);
701 }
702 }
703 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400704}
705
706void
707NatPmp::processMappingRequestFailed(const Mapping& map)
708{
709 if (observer_ == nullptr)
710 return;
711
712 // Process the response on the context thread.
Sébastien Blinf6baf4b2024-01-03 15:51:36 -0500713 ioContext->post([w=weak(), map] {
714 if (auto shared = w.lock()) {
715 if (!shared->shutdownComplete_) {
716 shared->observer_->onMappingRequestFailed(map);
717 }
718 }
719 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400720}
721
722void
723NatPmp::processMappingRenewed(const Mapping& map)
724{
725 if (observer_ == nullptr)
726 return;
727
728 // Process the response on the context thread.
Sébastien Blinf6baf4b2024-01-03 15:51:36 -0500729 ioContext->post([w=weak(), map] {
730 if (auto shared = w.lock()) {
731 if (!shared->shutdownComplete_) {
732 shared->observer_->onMappingRenewed(shared->igd_, map);
733 }
734 }
735 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400736}
737
738void
739NatPmp::processMappingRemoved(const Mapping& map)
740{
741 if (observer_ == nullptr)
742 return;
743
744 // Process the response on the context thread.
Sébastien Blinf6baf4b2024-01-03 15:51:36 -0500745 ioContext->post([w=weak(), map] {
746 if (auto shared = w.lock()) {
747 if (!shared->shutdownComplete_) {
748 shared->observer_->onMappingRemoved(shared->igd_, map);
749 }
750 }
751 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400752}
753
754} // namespace upnp
Sébastien Blin464bdff2023-07-19 08:02:53 -0400755} // namespace dhtnet
Adrien Béraud612b55b2023-05-29 10:42:04 -0400756
757#endif //-- #if HAVE_LIBNATPMP