blob: 599205e2d990d1a102f523cf0e31804664cf5af3 [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
24NatPmp::NatPmp()
25{
Morteza Namvar5f639522023-07-04 17:08:58 -040026 // JAMI_DBG("NAT-PMP: Instance [%p] created", this);
Adrien Béraud612b55b2023-05-29 10:42:04 -040027 runOnNatPmpQueue([this] {
28 threadId_ = getCurrentThread();
29 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{
41 if (not isValidThread()) {
42 runOnNatPmpQueue([w = weak()] {
43 if (auto pmpThis = w.lock()) {
44 pmpThis->initNatPmp();
45 }
46 });
47 return;
48 }
49
50 initialized_ = false;
51
52 {
53 std::lock_guard<std::mutex> lock(natpmpMutex_);
54 hostAddress_ = ip_utils::getLocalAddr(AF_INET);
55 }
56
57 // Local address must be valid.
58 if (not getHostAddress() or getHostAddress().isLoopback()) {
Morteza Namvar5f639522023-07-04 17:08:58 -040059 // JAMI_WARN("NAT-PMP: Does not have a valid local address!");
Adrien Béraud612b55b2023-05-29 10:42:04 -040060 return;
61 }
62
63 assert(igd_);
64 if (igd_->isValid()) {
65 igd_->setValid(false);
66 processIgdUpdate(UpnpIgdEvent::REMOVED);
67 }
68
69 igd_->setLocalIp(IpAddr());
70 igd_->setPublicIp(IpAddr());
71 igd_->setUID("");
72
Morteza Namvar5f639522023-07-04 17:08:58 -040073 // JAMI_DBG("NAT-PMP: Trying to initialize IGD");
Adrien Béraud612b55b2023-05-29 10:42:04 -040074
75 int err = initnatpmp(&natpmpHdl_, 0, 0);
76
77 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -040078 // JAMI_WARN("NAT-PMP: Initializing IGD using default gateway failed!");
Adrien Béraud612b55b2023-05-29 10:42:04 -040079 const auto& localGw = ip_utils::getLocalGateway();
80 if (not localGw) {
Morteza Namvar5f639522023-07-04 17:08:58 -040081 // JAMI_WARN("NAT-PMP: Couldn't find valid gateway on local host");
Adrien Béraud612b55b2023-05-29 10:42:04 -040082 err = NATPMP_ERR_CANNOTGETGATEWAY;
83 } else {
Morteza Namvar5f639522023-07-04 17:08:58 -040084 // JAMI_WARN("NAT-PMP: Trying to initialize using detected gateway %s",
Adrien Béraud612b55b2023-05-29 10:42:04 -040085 localGw.toString().c_str());
86
87 struct in_addr inaddr;
88 inet_pton(AF_INET, localGw.toString().c_str(), &inaddr);
89 err = initnatpmp(&natpmpHdl_, 1, inaddr.s_addr);
90 }
91 }
92
93 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -040094 // JAMI_ERR("NAT-PMP: Can't initialize libnatpmp -> %s", getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -040095 return;
96 }
97
98 char addrbuf[INET_ADDRSTRLEN];
99 inet_ntop(AF_INET, &natpmpHdl_.gateway, addrbuf, sizeof(addrbuf));
100 IpAddr igdAddr(addrbuf);
Morteza Namvar5f639522023-07-04 17:08:58 -0400101 // JAMI_DBG("NAT-PMP: Initialized on gateway %s", igdAddr.toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400102
103 // Set the local (gateway) address.
104 igd_->setLocalIp(igdAddr);
105 // NAT-PMP protocol does not have UID, but we will set generic
106 // one debugging purposes.
107 igd_->setUID("NAT-PMP Gateway");
108
109 // Search and set the public address.
110 getIgdPublicAddress();
111
112 // Update and notify.
113 if (igd_->isValid()) {
114 initialized_ = true;
115 processIgdUpdate(UpnpIgdEvent::ADDED);
116 };
117}
118
119void
120NatPmp::setObserver(UpnpMappingObserver* obs)
121{
122 if (not isValidThread()) {
123 runOnNatPmpQueue([w = weak(), obs] {
124 if (auto pmpThis = w.lock()) {
125 pmpThis->setObserver(obs);
126 }
127 });
128 return;
129 }
130
Morteza Namvar5f639522023-07-04 17:08:58 -0400131 // JAMI_DBG("NAT-PMP: Setting observer to %p", obs);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400132
133 observer_ = obs;
134}
135
136void
137NatPmp::terminate(std::condition_variable& cv)
138{
139 initialized_ = false;
140 observer_ = nullptr;
141
142 {
143 std::lock_guard<std::mutex> lock(natpmpMutex_);
144 shutdownComplete_ = true;
145 cv.notify_one();
146 }
147}
148
149void
150NatPmp::terminate()
151{
152 std::unique_lock<std::mutex> lk(natpmpMutex_);
153 std::condition_variable cv {};
154
155 runOnNatPmpQueue([w = weak(), &cv = cv] {
156 if (auto pmpThis = w.lock()) {
157 pmpThis->terminate(cv);
158 }
159 });
160
161 if (cv.wait_for(lk, std::chrono::seconds(10), [this] { return shutdownComplete_; })) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400162 // JAMI_DBG("NAT-PMP: Shutdown completed");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400163 } else {
Morteza Namvar5f639522023-07-04 17:08:58 -0400164 // JAMI_ERR("NAT-PMP: Shutdown timed-out");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400165 }
166}
167
168const IpAddr
169NatPmp::getHostAddress() const
170{
171 std::lock_guard<std::mutex> lock(natpmpMutex_);
172 return hostAddress_;
173}
174
175void
176NatPmp::clearIgds()
177{
178 if (not isValidThread()) {
179 runOnNatPmpQueue([w = weak()] {
180 if (auto pmpThis = w.lock()) {
181 pmpThis->clearIgds();
182 }
183 });
184 return;
185 }
186
187 bool do_close = false;
188
189 if (igd_) {
190 if (igd_->isValid()) {
191 do_close = true;
192 }
193 igd_->setValid(false);
194 }
195
196 initialized_ = false;
197 if (searchForIgdTimer_)
198 searchForIgdTimer_->cancel();
199
200 igdSearchCounter_ = 0;
201
202 if (do_close) {
203 closenatpmp(&natpmpHdl_);
204 memset(&natpmpHdl_, 0, sizeof(natpmpHdl_));
205 }
206}
207
208void
209NatPmp::searchForIgd()
210{
211 if (not isValidThread()) {
212 runOnNatPmpQueue([w = weak()] {
213 if (auto pmpThis = w.lock()) {
214 pmpThis->searchForIgd();
215 }
216 });
217 return;
218 }
219
220 if (not initialized_) {
221 initNatPmp();
222 }
223
224 // Schedule a retry in case init failed.
225 if (not initialized_) {
226 if (igdSearchCounter_++ < MAX_RESTART_SEARCH_RETRIES) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400227 // JAMI_DBG("NAT-PMP: Start search for IGDs. Attempt %i", igdSearchCounter_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400228
229 // Cancel the current timer (if any) and re-schedule.
230 if (searchForIgdTimer_)
231 searchForIgdTimer_->cancel();
232
233 searchForIgdTimer_ = getNatpmpScheduler()->scheduleIn([this] { searchForIgd(); },
234 NATPMP_SEARCH_RETRY_UNIT
235 * igdSearchCounter_);
236 } else {
Morteza Namvar5f639522023-07-04 17:08:58 -0400237 // JAMI_WARN("NAT-PMP: Setup failed after %u trials. NAT-PMP will be disabled!",
238 // MAX_RESTART_SEARCH_RETRIES);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400239 }
240 }
241}
242
243std::list<std::shared_ptr<IGD>>
244NatPmp::getIgdList() const
245{
246 std::lock_guard<std::mutex> lock(natpmpMutex_);
247 std::list<std::shared_ptr<IGD>> igdList;
248 if (igd_->isValid())
249 igdList.emplace_back(igd_);
250 return igdList;
251}
252
253bool
254NatPmp::isReady() const
255{
256 if (observer_ == nullptr) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400257 // JAMI_ERR("NAT-PMP: the observer is not set!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400258 return false;
259 }
260
261 // Must at least have a valid local address.
262 if (not getHostAddress() or getHostAddress().isLoopback())
263 return false;
264
265 return igd_ and igd_->isValid();
266}
267
268void
269NatPmp::incrementErrorsCounter(const std::shared_ptr<IGD>& igdIn)
270{
271 if (not validIgdInstance(igdIn)) {
272 return;
273 }
274
275 if (not igd_->isValid()) {
276 // Already invalid. Nothing to do.
277 return;
278 }
279
280 if (not igd_->incrementErrorsCounter()) {
281 // Disable this IGD.
282 igd_->setValid(false);
283 // Notify the listener.
Morteza Namvar5f639522023-07-04 17:08:58 -0400284 // JAMI_WARN("NAT-PMP: No more valid IGD!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400285
286 processIgdUpdate(UpnpIgdEvent::INVALID_STATE);
287 }
288}
289
290void
291NatPmp::requestMappingAdd(const Mapping& mapping)
292{
293 // Process on nat-pmp thread.
294 if (not isValidThread()) {
295 runOnNatPmpQueue([w = weak(), mapping] {
296 if (auto pmpThis = w.lock()) {
297 pmpThis->requestMappingAdd(mapping);
298 }
299 });
300 return;
301 }
302
303 Mapping map(mapping);
304 assert(map.getIgd());
305 auto err = addPortMapping(map);
306 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400307 // JAMI_WARN("NAT-PMP: Request for mapping %s on %s failed with error %i: %s",
308 // map.toString().c_str(),
309 // igd_->toString().c_str(),
310 // err,
311 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400312
313 if (isErrorFatal(err)) {
314 // Fatal error, increment the counter.
315 incrementErrorsCounter(igd_);
316 }
317 // Notify the listener.
318 processMappingRequestFailed(std::move(map));
319 } else {
Morteza Namvar5f639522023-07-04 17:08:58 -0400320 // JAMI_DBG("NAT-PMP: Request for mapping %s on %s succeeded",
321 // map.toString().c_str(),
322 // igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400323 // Notify the listener.
324 processMappingAdded(std::move(map));
325 }
326}
327
328void
329NatPmp::requestMappingRenew(const Mapping& mapping)
330{
331 // Process on nat-pmp thread.
332 if (not isValidThread()) {
333 runOnNatPmpQueue([w = weak(), mapping] {
334 if (auto pmpThis = w.lock()) {
335 pmpThis->requestMappingRenew(mapping);
336 }
337 });
338 return;
339 }
340
341 Mapping map(mapping);
342 auto err = addPortMapping(map);
343 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400344 // JAMI_WARN("NAT-PMP: Renewal request for mapping %s on %s failed with error %i: %s",
345 // map.toString().c_str(),
346 // igd_->toString().c_str(),
347 // err,
348 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400349 // Notify the listener.
350 processMappingRequestFailed(std::move(map));
351
352 if (isErrorFatal(err)) {
353 // Fatal error, increment the counter.
354 incrementErrorsCounter(igd_);
355 }
356 } else {
Morteza Namvar5f639522023-07-04 17:08:58 -0400357 // JAMI_DBG("NAT-PMP: Renewal request for mapping %s on %s succeeded",
358 // map.toString().c_str(),
359 // igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400360 // Notify the listener.
361 processMappingRenewed(map);
362 }
363}
364
365int
366NatPmp::readResponse(natpmp_t& handle, natpmpresp_t& response)
367{
368 int err = 0;
369 unsigned readRetriesCounter = 0;
370
371 while (true) {
372 if (readRetriesCounter++ > MAX_READ_RETRIES) {
373 err = NATPMP_ERR_SOCKETERROR;
374 break;
375 }
376
377 fd_set fds;
378 struct timeval timeout;
379 FD_ZERO(&fds);
380 FD_SET(handle.s, &fds);
381 getnatpmprequesttimeout(&handle, &timeout);
382 // Wait for data.
383 if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) == -1) {
384 err = NATPMP_ERR_SOCKETERROR;
385 break;
386 }
387
388 // Read the data.
389 err = readnatpmpresponseorretry(&handle, &response);
390
391 if (err == NATPMP_TRYAGAIN) {
392 std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT_BEFORE_READ_RETRY));
393 } else {
394 break;
395 }
396 }
397
398 return err;
399}
400
401int
402NatPmp::sendMappingRequest(const Mapping& mapping, uint32_t& lifetime)
403{
404 CHECK_VALID_THREAD();
405
406 int err = sendnewportmappingrequest(&natpmpHdl_,
407 mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP
408 : NATPMP_PROTOCOL_TCP,
409 mapping.getInternalPort(),
410 mapping.getExternalPort(),
411 lifetime);
412
413 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400414 // JAMI_ERR("NAT-PMP: Send mapping request failed with error %s %i",
415 // getNatPmpErrorStr(err),
416 // errno);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400417 return err;
418 }
419
420 unsigned readRetriesCounter = 0;
421
422 while (readRetriesCounter++ < MAX_READ_RETRIES) {
423 // Read the response
424 natpmpresp_t response;
425 err = readResponse(natpmpHdl_, response);
426
427 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400428 // JAMI_WARN("NAT-PMP: Read response on IGD %s failed with error %s",
429 // igd_->toString().c_str(),
430 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400431 } else if (response.type != NATPMP_RESPTYPE_TCPPORTMAPPING
432 and response.type != NATPMP_RESPTYPE_UDPPORTMAPPING) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400433 // JAMI_ERR("NAT-PMP: Unexpected response type (%i) for mapping %s from IGD %s.",
434 // response.type,
435 // mapping.toString().c_str(),
436 // igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400437 // Try to read again.
438 continue;
439 }
440
441 lifetime = response.pnu.newportmapping.lifetime;
442 // Done.
443 break;
444 }
445
446 return err;
447}
448
449int
450NatPmp::addPortMapping(Mapping& mapping)
451{
452 auto const& igdIn = mapping.getIgd();
453 assert(igdIn);
454 assert(igdIn->getProtocol() == NatProtocolType::NAT_PMP);
455
456 if (not igdIn->isValid() or not validIgdInstance(igdIn)) {
457 mapping.setState(MappingState::FAILED);
458 return NATPMP_ERR_INVALIDARGS;
459 }
460
461 mapping.setInternalAddress(getHostAddress().toString());
462
463 uint32_t lifetime = MAPPING_ALLOCATION_LIFETIME;
464 int err = sendMappingRequest(mapping, lifetime);
465
466 if (err < 0) {
467 mapping.setState(MappingState::FAILED);
468 return err;
469 }
470
471 // Set the renewal time and update.
472 mapping.setRenewalTime(sys_clock::now() + std::chrono::seconds(lifetime * 4 / 5));
473 mapping.setState(MappingState::OPEN);
474
475 return 0;
476}
477
478void
479NatPmp::requestMappingRemove(const Mapping& mapping)
480{
481 // Process on nat-pmp thread.
482 if (not isValidThread()) {
483 runOnNatPmpQueue([w = weak(), mapping] {
484 if (auto pmpThis = w.lock()) {
485 Mapping map {mapping};
486 pmpThis->removePortMapping(map);
487 }
488 });
489 return;
490 }
491}
492
493void
494NatPmp::removePortMapping(Mapping& mapping)
495{
496 auto igdIn = mapping.getIgd();
497 assert(igdIn);
498 if (not igdIn->isValid()) {
499 return;
500 }
501
502 if (not validIgdInstance(igdIn)) {
503 return;
504 }
505
506 Mapping mapToRemove(mapping);
507
508 uint32_t lifetime = 0;
509 int err = sendMappingRequest(mapping, lifetime);
510
511 if (err < 0) {
512 // Nothing to do if the request fails, just log the error.
Morteza Namvar5f639522023-07-04 17:08:58 -0400513 // JAMI_WARN("NAT-PMP: Send remove request failed with error %s. Ignoring",
514 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400515 }
516
517 // Update and notify the listener.
518 mapToRemove.setState(MappingState::FAILED);
519 processMappingRemoved(std::move(mapToRemove));
520}
521
522void
523NatPmp::getIgdPublicAddress()
524{
525 CHECK_VALID_THREAD();
526
527 // Set the public address for this IGD if it does not
528 // have one already.
529 if (igd_->getPublicIp()) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400530 // JAMI_WARN("NAT-PMP: IGD %s already have a public address (%s)",
531 // igd_->toString().c_str(),
532 // igd_->getPublicIp().toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400533 return;
534 }
535 assert(igd_->getProtocol() == NatProtocolType::NAT_PMP);
536
537 int err = sendpublicaddressrequest(&natpmpHdl_);
538
539 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400540 // JAMI_ERR("NAT-PMP: send public address request on IGD %s failed with error: %s",
541 // igd_->toString().c_str(),
542 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400543
544 if (isErrorFatal(err)) {
545 // Fatal error, increment the counter.
546 incrementErrorsCounter(igd_);
547 }
548 return;
549 }
550
551 natpmpresp_t response;
552 err = readResponse(natpmpHdl_, response);
553
554 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400555 // JAMI_WARN("NAT-PMP: Read response on IGD %s failed - %s",
556 // igd_->toString().c_str(),
557 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400558 return;
559 }
560
561 if (response.type != NATPMP_RESPTYPE_PUBLICADDRESS) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400562 // JAMI_ERR("NAT-PMP: Unexpected response type (%i) for public address request from IGD %s.",
563 // response.type,
564 // igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400565 return;
566 }
567
568 IpAddr publicAddr(response.pnu.publicaddress.addr);
569
570 if (not publicAddr) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400571 // JAMI_ERR("NAT-PMP: IGD %s returned an invalid public address %s",
572 // igd_->toString().c_str(),
573 // publicAddr.toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400574 }
575
576 // Update.
577 igd_->setPublicIp(publicAddr);
578 igd_->setValid(true);
579
Morteza Namvar5f639522023-07-04 17:08:58 -0400580 // JAMI_DBG("NAT-PMP: Setting IGD %s public address to %s",
581 // igd_->toString().c_str(),
582 // igd_->getPublicIp().toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400583}
584
585void
586NatPmp::removeAllMappings()
587{
588 CHECK_VALID_THREAD();
589
Morteza Namvar5f639522023-07-04 17:08:58 -0400590 // JAMI_WARN("NAT-PMP: Send request to close all existing mappings to IGD %s",
591 // igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400592
593 int err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_TCP, 0, 0, 0);
594 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400595 // JAMI_WARN("NAT-PMP: Send close all TCP mappings request failed with error %s",
596 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400597 }
598 err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_UDP, 0, 0, 0);
599 if (err < 0) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400600 // JAMI_WARN("NAT-PMP: Send close all UDP mappings request failed with error %s",
601 // getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400602 }
603}
604
605const char*
606NatPmp::getNatPmpErrorStr(int errorCode) const
607{
608#ifdef ENABLE_STRNATPMPERR
609 return strnatpmperr(errorCode);
610#else
611 switch (errorCode) {
612 case NATPMP_ERR_INVALIDARGS:
613 return "INVALIDARGS";
614 break;
615 case NATPMP_ERR_SOCKETERROR:
616 return "SOCKETERROR";
617 break;
618 case NATPMP_ERR_CANNOTGETGATEWAY:
619 return "CANNOTGETGATEWAY";
620 break;
621 case NATPMP_ERR_CLOSEERR:
622 return "CLOSEERR";
623 break;
624 case NATPMP_ERR_RECVFROM:
625 return "RECVFROM";
626 break;
627 case NATPMP_ERR_NOPENDINGREQ:
628 return "NOPENDINGREQ";
629 break;
630 case NATPMP_ERR_NOGATEWAYSUPPORT:
631 return "NOGATEWAYSUPPORT";
632 break;
633 case NATPMP_ERR_CONNECTERR:
634 return "CONNECTERR";
635 break;
636 case NATPMP_ERR_WRONGPACKETSOURCE:
637 return "WRONGPACKETSOURCE";
638 break;
639 case NATPMP_ERR_SENDERR:
640 return "SENDERR";
641 break;
642 case NATPMP_ERR_FCNTLERROR:
643 return "FCNTLERROR";
644 break;
645 case NATPMP_ERR_GETTIMEOFDAYERR:
646 return "GETTIMEOFDAYERR";
647 break;
648 case NATPMP_ERR_UNSUPPORTEDVERSION:
649 return "UNSUPPORTEDVERSION";
650 break;
651 case NATPMP_ERR_UNSUPPORTEDOPCODE:
652 return "UNSUPPORTEDOPCODE";
653 break;
654 case NATPMP_ERR_UNDEFINEDERROR:
655 return "UNDEFINEDERROR";
656 break;
657 case NATPMP_ERR_NOTAUTHORIZED:
658 return "NOTAUTHORIZED";
659 break;
660 case NATPMP_ERR_NETWORKFAILURE:
661 return "NETWORKFAILURE";
662 break;
663 case NATPMP_ERR_OUTOFRESOURCES:
664 return "OUTOFRESOURCES";
665 break;
666 case NATPMP_TRYAGAIN:
667 return "TRYAGAIN";
668 break;
669 default:
670 return "UNKNOWNERR";
671 break;
672 }
673#endif
674}
675
676bool
677NatPmp::isErrorFatal(int error)
678{
679 switch (error) {
680 case NATPMP_ERR_INVALIDARGS:
681 case NATPMP_ERR_SOCKETERROR:
682 case NATPMP_ERR_CANNOTGETGATEWAY:
683 case NATPMP_ERR_CLOSEERR:
684 case NATPMP_ERR_RECVFROM:
685 case NATPMP_ERR_NOGATEWAYSUPPORT:
686 case NATPMP_ERR_CONNECTERR:
687 case NATPMP_ERR_SENDERR:
688 case NATPMP_ERR_UNDEFINEDERROR:
689 case NATPMP_ERR_UNSUPPORTEDVERSION:
690 case NATPMP_ERR_UNSUPPORTEDOPCODE:
691 case NATPMP_ERR_NOTAUTHORIZED:
692 case NATPMP_ERR_NETWORKFAILURE:
693 case NATPMP_ERR_OUTOFRESOURCES:
694 return true;
695 default:
696 return false;
697 }
698}
699
700bool
701NatPmp::validIgdInstance(const std::shared_ptr<IGD>& igdIn)
702{
703 if (igd_.get() != igdIn.get()) {
Morteza Namvar5f639522023-07-04 17:08:58 -0400704 // JAMI_ERR("NAT-PMP: IGD (%s) does not match local instance (%s)",
705 // igdIn->toString().c_str(),
706 // igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400707 return false;
708 }
709
710 return true;
711}
712
713void
714NatPmp::processIgdUpdate(UpnpIgdEvent event)
715{
716 if (igd_->isValid()) {
717 // Remove all current mappings if any.
718 removeAllMappings();
719 }
720
721 if (observer_ == nullptr)
722 return;
723 // Process the response on the context thread.
724 runOnUpnpContextQueue([obs = observer_, igd = igd_, event] { obs->onIgdUpdated(igd, event); });
725}
726
727void
728NatPmp::processMappingAdded(const Mapping& map)
729{
730 if (observer_ == nullptr)
731 return;
732
733 // Process the response on the context thread.
734 runOnUpnpContextQueue([obs = observer_, igd = igd_, map] { obs->onMappingAdded(igd, map); });
735}
736
737void
738NatPmp::processMappingRequestFailed(const Mapping& map)
739{
740 if (observer_ == nullptr)
741 return;
742
743 // Process the response on the context thread.
744 runOnUpnpContextQueue([obs = observer_, igd = igd_, map] { obs->onMappingRequestFailed(map); });
745}
746
747void
748NatPmp::processMappingRenewed(const Mapping& map)
749{
750 if (observer_ == nullptr)
751 return;
752
753 // Process the response on the context thread.
754 runOnUpnpContextQueue([obs = observer_, igd = igd_, map] { obs->onMappingRenewed(igd, map); });
755}
756
757void
758NatPmp::processMappingRemoved(const Mapping& map)
759{
760 if (observer_ == nullptr)
761 return;
762
763 // Process the response on the context thread.
764 runOnUpnpContextQueue([obs = observer_, igd = igd_, map] { obs->onMappingRemoved(igd, map); });
765}
766
767} // namespace upnp
Sébastien Blin464bdff2023-07-19 08:02:53 -0400768} // namespace dhtnet
Adrien Béraud612b55b2023-05-29 10:42:04 -0400769
770#endif //-- #if HAVE_LIBNATPMP