blob: 633f5807ce510fc53662759a74a337332232a70d [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"
18
19#if HAVE_LIBNATPMP
20
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040021namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040022namespace upnp {
23
Adrien Béraud370257c2023-08-15 20:53:09 -040024NatPmp::NatPmp(const std::shared_ptr<asio::io_context>& ctx, const std::shared_ptr<dht::log::Logger>& logger)
25 : UPnPProtocol(logger), ioContext(ctx), searchForIgdTimer_(*ctx)
Adrien Béraud612b55b2023-05-29 10:42:04 -040026{
Morteza Namvar5f639522023-07-04 17:08:58 -040027 // JAMI_DBG("NAT-PMP: Instance [%p] created", this);
Adrien Béraud370257c2023-08-15 20:53:09 -040028 ioContext->dispatch([this] {
Adrien Béraud612b55b2023-05-29 10:42:04 -040029 igd_ = std::make_shared<PMPIGD>();
30 });
31}
32
33NatPmp::~NatPmp()
34{
Morteza Namvar5f639522023-07-04 17:08:58 -040035 // JAMI_DBG("NAT-PMP: Instance [%p] destroyed", this);
Adrien Béraud612b55b2023-05-29 10:42:04 -040036}
37
38void
39NatPmp::initNatPmp()
40{
Adrien Béraud612b55b2023-05-29 10:42:04 -040041 initialized_ = false;
42
43 {
44 std::lock_guard<std::mutex> lock(natpmpMutex_);
45 hostAddress_ = ip_utils::getLocalAddr(AF_INET);
46 }
47
48 // Local address must be valid.
49 if (not getHostAddress() or getHostAddress().isLoopback()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040050 if (logger_) logger_->warn("NAT-PMP: Does not have a valid local address!");
Adrien Béraud612b55b2023-05-29 10:42:04 -040051 return;
52 }
53
54 assert(igd_);
55 if (igd_->isValid()) {
56 igd_->setValid(false);
57 processIgdUpdate(UpnpIgdEvent::REMOVED);
58 }
59
60 igd_->setLocalIp(IpAddr());
61 igd_->setPublicIp(IpAddr());
62 igd_->setUID("");
63
Adrien Béraud17765bd2023-08-22 21:02:38 -040064 if (logger_) logger_->debug("NAT-PMP: Trying to initialize IGD");
Adrien Béraud612b55b2023-05-29 10:42:04 -040065
66 int err = initnatpmp(&natpmpHdl_, 0, 0);
67
68 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040069 if (logger_) logger_->warn("NAT-PMP: Initializing IGD using default gateway failed!");
Adrien Béraud612b55b2023-05-29 10:42:04 -040070 const auto& localGw = ip_utils::getLocalGateway();
71 if (not localGw) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040072 if (logger_) logger_->warn("NAT-PMP: Couldn't find valid gateway on local host");
Adrien Béraud612b55b2023-05-29 10:42:04 -040073 err = NATPMP_ERR_CANNOTGETGATEWAY;
74 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -040075 if (logger_) logger_->warn("NAT-PMP: Trying to initialize using detected gateway {}",
76 localGw.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -040077 struct in_addr inaddr;
78 inet_pton(AF_INET, localGw.toString().c_str(), &inaddr);
79 err = initnatpmp(&natpmpHdl_, 1, inaddr.s_addr);
80 }
81 }
82
83 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -040084 if (logger_) logger_->error("NAT-PMP: Can't initialize libnatpmp -> {}", getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -040085 return;
86 }
87
88 char addrbuf[INET_ADDRSTRLEN];
89 inet_ntop(AF_INET, &natpmpHdl_.gateway, addrbuf, sizeof(addrbuf));
90 IpAddr igdAddr(addrbuf);
Adrien Béraud17765bd2023-08-22 21:02:38 -040091 if (logger_) logger_->debug("NAT-PMP: Initialized on gateway {}", igdAddr.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -040092
93 // Set the local (gateway) address.
94 igd_->setLocalIp(igdAddr);
95 // NAT-PMP protocol does not have UID, but we will set generic
96 // one debugging purposes.
97 igd_->setUID("NAT-PMP Gateway");
98
99 // Search and set the public address.
100 getIgdPublicAddress();
101
102 // Update and notify.
103 if (igd_->isValid()) {
104 initialized_ = true;
105 processIgdUpdate(UpnpIgdEvent::ADDED);
106 };
107}
108
109void
110NatPmp::setObserver(UpnpMappingObserver* obs)
111{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400112 observer_ = obs;
113}
114
115void
116NatPmp::terminate(std::condition_variable& cv)
117{
118 initialized_ = false;
119 observer_ = nullptr;
120
121 {
122 std::lock_guard<std::mutex> lock(natpmpMutex_);
123 shutdownComplete_ = true;
124 cv.notify_one();
125 }
126}
127
128void
129NatPmp::terminate()
130{
131 std::unique_lock<std::mutex> lk(natpmpMutex_);
132 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
138 if (cv.wait_for(lk, std::chrono::seconds(10), [this] { return shutdownComplete_; })) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400139 if (logger_) logger_->debug("NAT-PMP: Shutdown completed");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400140 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400141 if (logger_) logger_->error("NAT-PMP: Shutdown timed-out");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400142 }
143}
144
145const IpAddr
146NatPmp::getHostAddress() const
147{
148 std::lock_guard<std::mutex> lock(natpmpMutex_);
149 return hostAddress_;
150}
151
152void
153NatPmp::clearIgds()
154{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400155 bool do_close = false;
156
157 if (igd_) {
158 if (igd_->isValid()) {
159 do_close = true;
160 }
161 igd_->setValid(false);
162 }
163
164 initialized_ = false;
Adrien Béraud370257c2023-08-15 20:53:09 -0400165 searchForIgdTimer_.cancel();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400166
167 igdSearchCounter_ = 0;
168
169 if (do_close) {
170 closenatpmp(&natpmpHdl_);
171 memset(&natpmpHdl_, 0, sizeof(natpmpHdl_));
172 }
173}
174
175void
176NatPmp::searchForIgd()
177{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400178 if (not initialized_) {
179 initNatPmp();
180 }
181
182 // Schedule a retry in case init failed.
183 if (not initialized_) {
184 if (igdSearchCounter_++ < MAX_RESTART_SEARCH_RETRIES) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400185 if (logger_) logger_->debug("NAT-PMP: Start search for IGDs. Attempt {}", igdSearchCounter_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400186
187 // Cancel the current timer (if any) and re-schedule.
Adrien Béraud370257c2023-08-15 20:53:09 -0400188 searchForIgdTimer_.expires_after(NATPMP_SEARCH_RETRY_UNIT * igdSearchCounter_);
189 searchForIgdTimer_.async_wait([this](const asio::error_code& ec) {
190 if (!ec)
191 searchForIgd();
192 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400193 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400194 if (logger_) logger_->warn("NAT-PMP: Setup failed after {} trials. NAT-PMP will be disabled!",
195 MAX_RESTART_SEARCH_RETRIES);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400196 }
197 }
198}
199
200std::list<std::shared_ptr<IGD>>
201NatPmp::getIgdList() const
202{
203 std::lock_guard<std::mutex> lock(natpmpMutex_);
204 std::list<std::shared_ptr<IGD>> igdList;
205 if (igd_->isValid())
206 igdList.emplace_back(igd_);
207 return igdList;
208}
209
210bool
211NatPmp::isReady() const
212{
213 if (observer_ == nullptr) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400214 if (logger_) logger_->error("NAT-PMP: the observer is not set!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400215 return false;
216 }
217
218 // Must at least have a valid local address.
219 if (not getHostAddress() or getHostAddress().isLoopback())
220 return false;
221
222 return igd_ and igd_->isValid();
223}
224
225void
226NatPmp::incrementErrorsCounter(const std::shared_ptr<IGD>& igdIn)
227{
228 if (not validIgdInstance(igdIn)) {
229 return;
230 }
231
232 if (not igd_->isValid()) {
233 // Already invalid. Nothing to do.
234 return;
235 }
236
237 if (not igd_->incrementErrorsCounter()) {
238 // Disable this IGD.
239 igd_->setValid(false);
240 // Notify the listener.
Adrien Béraud17765bd2023-08-22 21:02:38 -0400241 if (logger_) logger_->warn("NAT-PMP: No more valid IGD!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400242
243 processIgdUpdate(UpnpIgdEvent::INVALID_STATE);
244 }
245}
246
247void
248NatPmp::requestMappingAdd(const Mapping& mapping)
249{
250 // Process on nat-pmp thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400251 /*if (not isValidThread()) {
252 ioContext->post([w = weak(), mapping] {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400253 if (auto pmpThis = w.lock()) {
254 pmpThis->requestMappingAdd(mapping);
255 }
256 });
257 return;
Adrien Béraud370257c2023-08-15 20:53:09 -0400258 }*/
Adrien Béraud612b55b2023-05-29 10:42:04 -0400259
260 Mapping map(mapping);
261 assert(map.getIgd());
262 auto err = addPortMapping(map);
263 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400264 if (logger_) logger_->warn("NAT-PMP: Request for mapping {} on {} failed with error {:d}: {}",
265 map.toString(),
266 igd_->toString(),
267 err,
268 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400269
270 if (isErrorFatal(err)) {
271 // Fatal error, increment the counter.
272 incrementErrorsCounter(igd_);
273 }
274 // Notify the listener.
275 processMappingRequestFailed(std::move(map));
276 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400277 if (logger_) logger_->debug("NAT-PMP: Request for mapping {:s} on {:s} succeeded",
278 map.toString(),
279 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400280 // Notify the listener.
281 processMappingAdded(std::move(map));
282 }
283}
284
285void
286NatPmp::requestMappingRenew(const Mapping& mapping)
287{
288 // Process on nat-pmp thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400289 /*if (not isValidThread()) {
290 ioContext->post([w = weak(), mapping] {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400291 if (auto pmpThis = w.lock()) {
292 pmpThis->requestMappingRenew(mapping);
293 }
294 });
295 return;
Adrien Béraud370257c2023-08-15 20:53:09 -0400296 }*/
Adrien Béraud612b55b2023-05-29 10:42:04 -0400297
298 Mapping map(mapping);
299 auto err = addPortMapping(map);
300 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400301 if (logger_) logger_->warn("NAT-PMP: Renewal request for mapping {} on {} failed with error {:d}: {}",
302 map.toString().c_str(),
303 igd_->toString().c_str(),
304 err,
305 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400306 // Notify the listener.
307 processMappingRequestFailed(std::move(map));
308
309 if (isErrorFatal(err)) {
310 // Fatal error, increment the counter.
311 incrementErrorsCounter(igd_);
312 }
313 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400314 if (logger_) logger_->debug("NAT-PMP: Renewal request for mapping {} on {} succeeded",
315 map.toString().c_str(),
316 igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400317 // Notify the listener.
318 processMappingRenewed(map);
319 }
320}
321
322int
323NatPmp::readResponse(natpmp_t& handle, natpmpresp_t& response)
324{
325 int err = 0;
326 unsigned readRetriesCounter = 0;
327
328 while (true) {
329 if (readRetriesCounter++ > MAX_READ_RETRIES) {
330 err = NATPMP_ERR_SOCKETERROR;
331 break;
332 }
333
334 fd_set fds;
335 struct timeval timeout;
336 FD_ZERO(&fds);
337 FD_SET(handle.s, &fds);
338 getnatpmprequesttimeout(&handle, &timeout);
339 // Wait for data.
340 if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) == -1) {
341 err = NATPMP_ERR_SOCKETERROR;
342 break;
343 }
344
345 // Read the data.
346 err = readnatpmpresponseorretry(&handle, &response);
347
348 if (err == NATPMP_TRYAGAIN) {
349 std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT_BEFORE_READ_RETRY));
350 } else {
351 break;
352 }
353 }
354
355 return err;
356}
357
358int
359NatPmp::sendMappingRequest(const Mapping& mapping, uint32_t& lifetime)
360{
Adrien Béraud370257c2023-08-15 20:53:09 -0400361 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400362
363 int err = sendnewportmappingrequest(&natpmpHdl_,
364 mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP
365 : NATPMP_PROTOCOL_TCP,
366 mapping.getInternalPort(),
367 mapping.getExternalPort(),
368 lifetime);
369
370 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400371 if (logger_) logger_->error("NAT-PMP: Send mapping request failed with error {} {:d}",
372 getNatPmpErrorStr(err),
373 errno);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400374 return err;
375 }
376
377 unsigned readRetriesCounter = 0;
378
379 while (readRetriesCounter++ < MAX_READ_RETRIES) {
380 // Read the response
381 natpmpresp_t response;
382 err = readResponse(natpmpHdl_, response);
383
384 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400385 if (logger_) logger_->warn("NAT-PMP: Read response on IGD {} failed with error {}",
386 igd_->toString(),
387 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400388 } else if (response.type != NATPMP_RESPTYPE_TCPPORTMAPPING
389 and response.type != NATPMP_RESPTYPE_UDPPORTMAPPING) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400390 if (logger_) logger_->error("NAT-PMP: Unexpected response type ({:d}) for mapping {} from IGD {}.",
391 response.type,
392 mapping.toString(),
393 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400394 // Try to read again.
395 continue;
396 }
397
398 lifetime = response.pnu.newportmapping.lifetime;
399 // Done.
400 break;
401 }
402
403 return err;
404}
405
406int
407NatPmp::addPortMapping(Mapping& mapping)
408{
409 auto const& igdIn = mapping.getIgd();
410 assert(igdIn);
411 assert(igdIn->getProtocol() == NatProtocolType::NAT_PMP);
412
413 if (not igdIn->isValid() or not validIgdInstance(igdIn)) {
414 mapping.setState(MappingState::FAILED);
415 return NATPMP_ERR_INVALIDARGS;
416 }
417
418 mapping.setInternalAddress(getHostAddress().toString());
419
420 uint32_t lifetime = MAPPING_ALLOCATION_LIFETIME;
421 int err = sendMappingRequest(mapping, lifetime);
422
423 if (err < 0) {
424 mapping.setState(MappingState::FAILED);
425 return err;
426 }
427
428 // Set the renewal time and update.
429 mapping.setRenewalTime(sys_clock::now() + std::chrono::seconds(lifetime * 4 / 5));
430 mapping.setState(MappingState::OPEN);
431
432 return 0;
433}
434
435void
436NatPmp::requestMappingRemove(const Mapping& mapping)
437{
Adrien Béraud370257c2023-08-15 20:53:09 -0400438 ioContext->dispatch([w = weak(), mapping] {
439 if (auto pmpThis = w.lock()) {
440 Mapping map {mapping};
441 pmpThis->removePortMapping(map);
442 }
443 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400444}
445
446void
447NatPmp::removePortMapping(Mapping& mapping)
448{
449 auto igdIn = mapping.getIgd();
450 assert(igdIn);
451 if (not igdIn->isValid()) {
452 return;
453 }
454
455 if (not validIgdInstance(igdIn)) {
456 return;
457 }
458
459 Mapping mapToRemove(mapping);
460
461 uint32_t lifetime = 0;
462 int err = sendMappingRequest(mapping, lifetime);
463
464 if (err < 0) {
465 // Nothing to do if the request fails, just log the error.
Adrien Béraud17765bd2023-08-22 21:02:38 -0400466 if (logger_) logger_->warn("NAT-PMP: Send remove request failed with error {}. Ignoring",
467 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400468 }
469
470 // Update and notify the listener.
471 mapToRemove.setState(MappingState::FAILED);
472 processMappingRemoved(std::move(mapToRemove));
473}
474
475void
476NatPmp::getIgdPublicAddress()
477{
Adrien Béraud370257c2023-08-15 20:53:09 -0400478 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400479
480 // Set the public address for this IGD if it does not
481 // have one already.
482 if (igd_->getPublicIp()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400483 if (logger_) logger_->warn("NAT-PMP: IGD {} already have a public address ({})",
484 igd_->toString(),
485 igd_->getPublicIp().toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400486 return;
487 }
488 assert(igd_->getProtocol() == NatProtocolType::NAT_PMP);
489
490 int err = sendpublicaddressrequest(&natpmpHdl_);
491
492 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400493 if (logger_) logger_->error("NAT-PMP: send public address request on IGD {} failed with error: {}",
494 igd_->toString(),
495 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400496
497 if (isErrorFatal(err)) {
498 // Fatal error, increment the counter.
499 incrementErrorsCounter(igd_);
500 }
501 return;
502 }
503
504 natpmpresp_t response;
505 err = readResponse(natpmpHdl_, response);
506
507 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400508 if (logger_) logger_->warn("NAT-PMP: Read response on IGD {} failed - {}",
509 igd_->toString(),
510 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400511 return;
512 }
513
514 if (response.type != NATPMP_RESPTYPE_PUBLICADDRESS) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400515 if (logger_) logger_->error("NAT-PMP: Unexpected response type ({:d}) for public address request from IGD {}.",
516 response.type,
517 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400518 return;
519 }
520
521 IpAddr publicAddr(response.pnu.publicaddress.addr);
522
523 if (not publicAddr) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400524 if (logger_) logger_->error("NAT-PMP: IGD {} returned an invalid public address {}",
525 igd_->toString(),
526 publicAddr.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400527 }
528
529 // Update.
530 igd_->setPublicIp(publicAddr);
531 igd_->setValid(true);
532
Adrien Béraud17765bd2023-08-22 21:02:38 -0400533 if (logger_) logger_->debug("NAT-PMP: Setting IGD {} public address to {}",
534 igd_->toString(),
535 igd_->getPublicIp().toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400536}
537
538void
539NatPmp::removeAllMappings()
540{
Adrien Béraud370257c2023-08-15 20:53:09 -0400541 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400542
Adrien Béraud17765bd2023-08-22 21:02:38 -0400543 if (logger_) logger_->warn("NAT-PMP: Send request to close all existing mappings to IGD {}",
544 igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400545
546 int err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_TCP, 0, 0, 0);
547 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400548 if (logger_) logger_->warn("NAT-PMP: Send close all TCP mappings request failed with error {}",
549 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400550 }
551 err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_UDP, 0, 0, 0);
552 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400553 if (logger_) logger_->warn("NAT-PMP: Send close all UDP mappings request failed with error {}",
554 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400555 }
556}
557
558const char*
559NatPmp::getNatPmpErrorStr(int errorCode) const
560{
561#ifdef ENABLE_STRNATPMPERR
562 return strnatpmperr(errorCode);
563#else
564 switch (errorCode) {
565 case NATPMP_ERR_INVALIDARGS:
566 return "INVALIDARGS";
567 break;
568 case NATPMP_ERR_SOCKETERROR:
569 return "SOCKETERROR";
570 break;
571 case NATPMP_ERR_CANNOTGETGATEWAY:
572 return "CANNOTGETGATEWAY";
573 break;
574 case NATPMP_ERR_CLOSEERR:
575 return "CLOSEERR";
576 break;
577 case NATPMP_ERR_RECVFROM:
578 return "RECVFROM";
579 break;
580 case NATPMP_ERR_NOPENDINGREQ:
581 return "NOPENDINGREQ";
582 break;
583 case NATPMP_ERR_NOGATEWAYSUPPORT:
584 return "NOGATEWAYSUPPORT";
585 break;
586 case NATPMP_ERR_CONNECTERR:
587 return "CONNECTERR";
588 break;
589 case NATPMP_ERR_WRONGPACKETSOURCE:
590 return "WRONGPACKETSOURCE";
591 break;
592 case NATPMP_ERR_SENDERR:
593 return "SENDERR";
594 break;
595 case NATPMP_ERR_FCNTLERROR:
596 return "FCNTLERROR";
597 break;
598 case NATPMP_ERR_GETTIMEOFDAYERR:
599 return "GETTIMEOFDAYERR";
600 break;
601 case NATPMP_ERR_UNSUPPORTEDVERSION:
602 return "UNSUPPORTEDVERSION";
603 break;
604 case NATPMP_ERR_UNSUPPORTEDOPCODE:
605 return "UNSUPPORTEDOPCODE";
606 break;
607 case NATPMP_ERR_UNDEFINEDERROR:
608 return "UNDEFINEDERROR";
609 break;
610 case NATPMP_ERR_NOTAUTHORIZED:
611 return "NOTAUTHORIZED";
612 break;
613 case NATPMP_ERR_NETWORKFAILURE:
614 return "NETWORKFAILURE";
615 break;
616 case NATPMP_ERR_OUTOFRESOURCES:
617 return "OUTOFRESOURCES";
618 break;
619 case NATPMP_TRYAGAIN:
620 return "TRYAGAIN";
621 break;
622 default:
623 return "UNKNOWNERR";
624 break;
625 }
626#endif
627}
628
629bool
630NatPmp::isErrorFatal(int error)
631{
632 switch (error) {
633 case NATPMP_ERR_INVALIDARGS:
634 case NATPMP_ERR_SOCKETERROR:
635 case NATPMP_ERR_CANNOTGETGATEWAY:
636 case NATPMP_ERR_CLOSEERR:
637 case NATPMP_ERR_RECVFROM:
638 case NATPMP_ERR_NOGATEWAYSUPPORT:
639 case NATPMP_ERR_CONNECTERR:
640 case NATPMP_ERR_SENDERR:
641 case NATPMP_ERR_UNDEFINEDERROR:
642 case NATPMP_ERR_UNSUPPORTEDVERSION:
643 case NATPMP_ERR_UNSUPPORTEDOPCODE:
644 case NATPMP_ERR_NOTAUTHORIZED:
645 case NATPMP_ERR_NETWORKFAILURE:
646 case NATPMP_ERR_OUTOFRESOURCES:
647 return true;
648 default:
649 return false;
650 }
651}
652
653bool
654NatPmp::validIgdInstance(const std::shared_ptr<IGD>& igdIn)
655{
656 if (igd_.get() != igdIn.get()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400657 if (logger_) logger_->error("NAT-PMP: IGD ({}) does not match local instance ({})",
658 igdIn->toString(),
659 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400660 return false;
661 }
662
663 return true;
664}
665
666void
667NatPmp::processIgdUpdate(UpnpIgdEvent event)
668{
669 if (igd_->isValid()) {
670 // Remove all current mappings if any.
671 removeAllMappings();
672 }
673
674 if (observer_ == nullptr)
675 return;
676 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400677 ioContext->post([obs = observer_, igd = igd_, event] { obs->onIgdUpdated(igd, event); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400678}
679
680void
681NatPmp::processMappingAdded(const Mapping& map)
682{
683 if (observer_ == nullptr)
684 return;
685
686 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400687 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingAdded(igd, map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400688}
689
690void
691NatPmp::processMappingRequestFailed(const Mapping& map)
692{
693 if (observer_ == nullptr)
694 return;
695
696 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400697 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingRequestFailed(map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400698}
699
700void
701NatPmp::processMappingRenewed(const Mapping& map)
702{
703 if (observer_ == nullptr)
704 return;
705
706 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400707 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingRenewed(igd, map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400708}
709
710void
711NatPmp::processMappingRemoved(const Mapping& map)
712{
713 if (observer_ == nullptr)
714 return;
715
716 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400717 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingRemoved(igd, map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400718}
719
720} // namespace upnp
Sébastien Blin464bdff2023-07-19 08:02:53 -0400721} // namespace dhtnet
Adrien Béraud612b55b2023-05-29 10:42:04 -0400722
723#endif //-- #if HAVE_LIBNATPMP