blob: 48e010a7d8530cacd36a6c062282cd28d66523f1 [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{
Adrien Béraud7a82bee2023-08-30 10:26:45 -0400118 if (logger_) logger_->debug("NAT-PMP: Terminate instance {}", fmt::ptr(this));
119
Adrien Béraud612b55b2023-05-29 10:42:04 -0400120 initialized_ = false;
121 observer_ = nullptr;
122
Adrien Béraud7a82bee2023-08-30 10:26:45 -0400123 std::lock_guard<std::mutex> lock(natpmpMutex_);
124 shutdownComplete_ = true;
125 cv.notify_one();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400126}
127
128void
129NatPmp::terminate()
130{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400131 std::condition_variable cv {};
132
Adrien Béraud370257c2023-08-15 20:53:09 -0400133 ioContext->dispatch([&] {
134 terminate(cv);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400135 });
136
Adrien Béraud7a82bee2023-08-30 10:26:45 -0400137 std::unique_lock<std::mutex> lk(natpmpMutex_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400138 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_);
Sébastien Blind1e018c2023-09-01 09:15:12 -0400189 searchForIgdTimer_.async_wait([w=weak()](const asio::error_code& ec) {
190 if (!ec) {
191 if (auto shared = w.lock())
192 shared->searchForIgd();
193 }
Adrien Béraud370257c2023-08-15 20:53:09 -0400194 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400195 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400196 if (logger_) logger_->warn("NAT-PMP: Setup failed after {} trials. NAT-PMP will be disabled!",
197 MAX_RESTART_SEARCH_RETRIES);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400198 }
199 }
200}
201
202std::list<std::shared_ptr<IGD>>
203NatPmp::getIgdList() const
204{
205 std::lock_guard<std::mutex> lock(natpmpMutex_);
206 std::list<std::shared_ptr<IGD>> igdList;
207 if (igd_->isValid())
208 igdList.emplace_back(igd_);
209 return igdList;
210}
211
212bool
213NatPmp::isReady() const
214{
215 if (observer_ == nullptr) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400216 if (logger_) logger_->error("NAT-PMP: the observer is not set!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400217 return false;
218 }
219
220 // Must at least have a valid local address.
221 if (not getHostAddress() or getHostAddress().isLoopback())
222 return false;
223
224 return igd_ and igd_->isValid();
225}
226
227void
228NatPmp::incrementErrorsCounter(const std::shared_ptr<IGD>& igdIn)
229{
230 if (not validIgdInstance(igdIn)) {
231 return;
232 }
233
234 if (not igd_->isValid()) {
235 // Already invalid. Nothing to do.
236 return;
237 }
238
239 if (not igd_->incrementErrorsCounter()) {
240 // Disable this IGD.
241 igd_->setValid(false);
242 // Notify the listener.
Adrien Béraud17765bd2023-08-22 21:02:38 -0400243 if (logger_) logger_->warn("NAT-PMP: No more valid IGD!");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400244
245 processIgdUpdate(UpnpIgdEvent::INVALID_STATE);
246 }
247}
248
249void
250NatPmp::requestMappingAdd(const Mapping& mapping)
251{
252 // Process on nat-pmp thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400253 /*if (not isValidThread()) {
254 ioContext->post([w = weak(), mapping] {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400255 if (auto pmpThis = w.lock()) {
256 pmpThis->requestMappingAdd(mapping);
257 }
258 });
259 return;
Adrien Béraud370257c2023-08-15 20:53:09 -0400260 }*/
Adrien Béraud612b55b2023-05-29 10:42:04 -0400261
262 Mapping map(mapping);
263 assert(map.getIgd());
264 auto err = addPortMapping(map);
265 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400266 if (logger_) logger_->warn("NAT-PMP: Request for mapping {} on {} failed with error {:d}: {}",
267 map.toString(),
268 igd_->toString(),
269 err,
270 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400271
272 if (isErrorFatal(err)) {
273 // Fatal error, increment the counter.
274 incrementErrorsCounter(igd_);
275 }
276 // Notify the listener.
277 processMappingRequestFailed(std::move(map));
278 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400279 if (logger_) logger_->debug("NAT-PMP: Request for mapping {:s} on {:s} succeeded",
280 map.toString(),
281 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400282 // Notify the listener.
283 processMappingAdded(std::move(map));
284 }
285}
286
287void
288NatPmp::requestMappingRenew(const Mapping& mapping)
289{
290 // Process on nat-pmp thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400291 /*if (not isValidThread()) {
292 ioContext->post([w = weak(), mapping] {
Adrien Béraud612b55b2023-05-29 10:42:04 -0400293 if (auto pmpThis = w.lock()) {
294 pmpThis->requestMappingRenew(mapping);
295 }
296 });
297 return;
Adrien Béraud370257c2023-08-15 20:53:09 -0400298 }*/
Adrien Béraud612b55b2023-05-29 10:42:04 -0400299
300 Mapping map(mapping);
301 auto err = addPortMapping(map);
302 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400303 if (logger_) logger_->warn("NAT-PMP: Renewal request for mapping {} on {} failed with error {:d}: {}",
304 map.toString().c_str(),
305 igd_->toString().c_str(),
306 err,
307 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400308 // Notify the listener.
309 processMappingRequestFailed(std::move(map));
310
311 if (isErrorFatal(err)) {
312 // Fatal error, increment the counter.
313 incrementErrorsCounter(igd_);
314 }
315 } else {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400316 if (logger_) logger_->debug("NAT-PMP: Renewal request for mapping {} on {} succeeded",
317 map.toString().c_str(),
318 igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400319 // Notify the listener.
320 processMappingRenewed(map);
321 }
322}
323
324int
325NatPmp::readResponse(natpmp_t& handle, natpmpresp_t& response)
326{
327 int err = 0;
328 unsigned readRetriesCounter = 0;
329
330 while (true) {
331 if (readRetriesCounter++ > MAX_READ_RETRIES) {
332 err = NATPMP_ERR_SOCKETERROR;
333 break;
334 }
335
336 fd_set fds;
337 struct timeval timeout;
338 FD_ZERO(&fds);
339 FD_SET(handle.s, &fds);
340 getnatpmprequesttimeout(&handle, &timeout);
341 // Wait for data.
342 if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) == -1) {
343 err = NATPMP_ERR_SOCKETERROR;
344 break;
345 }
346
347 // Read the data.
348 err = readnatpmpresponseorretry(&handle, &response);
349
350 if (err == NATPMP_TRYAGAIN) {
351 std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT_BEFORE_READ_RETRY));
352 } else {
353 break;
354 }
355 }
356
357 return err;
358}
359
360int
361NatPmp::sendMappingRequest(const Mapping& mapping, uint32_t& lifetime)
362{
Adrien Béraud370257c2023-08-15 20:53:09 -0400363 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400364
365 int err = sendnewportmappingrequest(&natpmpHdl_,
366 mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP
367 : NATPMP_PROTOCOL_TCP,
368 mapping.getInternalPort(),
369 mapping.getExternalPort(),
370 lifetime);
371
372 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400373 if (logger_) logger_->error("NAT-PMP: Send mapping request failed with error {} {:d}",
374 getNatPmpErrorStr(err),
375 errno);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400376 return err;
377 }
378
379 unsigned readRetriesCounter = 0;
380
381 while (readRetriesCounter++ < MAX_READ_RETRIES) {
382 // Read the response
383 natpmpresp_t response;
384 err = readResponse(natpmpHdl_, response);
385
386 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400387 if (logger_) logger_->warn("NAT-PMP: Read response on IGD {} failed with error {}",
388 igd_->toString(),
389 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400390 } else if (response.type != NATPMP_RESPTYPE_TCPPORTMAPPING
391 and response.type != NATPMP_RESPTYPE_UDPPORTMAPPING) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400392 if (logger_) logger_->error("NAT-PMP: Unexpected response type ({:d}) for mapping {} from IGD {}.",
393 response.type,
394 mapping.toString(),
395 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400396 // Try to read again.
397 continue;
398 }
399
400 lifetime = response.pnu.newportmapping.lifetime;
401 // Done.
402 break;
403 }
404
405 return err;
406}
407
408int
409NatPmp::addPortMapping(Mapping& mapping)
410{
411 auto const& igdIn = mapping.getIgd();
412 assert(igdIn);
413 assert(igdIn->getProtocol() == NatProtocolType::NAT_PMP);
414
415 if (not igdIn->isValid() or not validIgdInstance(igdIn)) {
416 mapping.setState(MappingState::FAILED);
417 return NATPMP_ERR_INVALIDARGS;
418 }
419
420 mapping.setInternalAddress(getHostAddress().toString());
421
422 uint32_t lifetime = MAPPING_ALLOCATION_LIFETIME;
423 int err = sendMappingRequest(mapping, lifetime);
424
425 if (err < 0) {
426 mapping.setState(MappingState::FAILED);
427 return err;
428 }
429
430 // Set the renewal time and update.
431 mapping.setRenewalTime(sys_clock::now() + std::chrono::seconds(lifetime * 4 / 5));
432 mapping.setState(MappingState::OPEN);
433
434 return 0;
435}
436
437void
438NatPmp::requestMappingRemove(const Mapping& mapping)
439{
Adrien Béraud370257c2023-08-15 20:53:09 -0400440 ioContext->dispatch([w = weak(), mapping] {
441 if (auto pmpThis = w.lock()) {
442 Mapping map {mapping};
443 pmpThis->removePortMapping(map);
444 }
445 });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400446}
447
448void
449NatPmp::removePortMapping(Mapping& mapping)
450{
451 auto igdIn = mapping.getIgd();
452 assert(igdIn);
453 if (not igdIn->isValid()) {
454 return;
455 }
456
457 if (not validIgdInstance(igdIn)) {
458 return;
459 }
460
461 Mapping mapToRemove(mapping);
462
463 uint32_t lifetime = 0;
464 int err = sendMappingRequest(mapping, lifetime);
465
466 if (err < 0) {
467 // Nothing to do if the request fails, just log the error.
Adrien Béraud17765bd2023-08-22 21:02:38 -0400468 if (logger_) logger_->warn("NAT-PMP: Send remove request failed with error {}. Ignoring",
469 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400470 }
471
472 // Update and notify the listener.
473 mapToRemove.setState(MappingState::FAILED);
474 processMappingRemoved(std::move(mapToRemove));
475}
476
477void
478NatPmp::getIgdPublicAddress()
479{
Adrien Béraud370257c2023-08-15 20:53:09 -0400480 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400481
482 // Set the public address for this IGD if it does not
483 // have one already.
484 if (igd_->getPublicIp()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400485 if (logger_) logger_->warn("NAT-PMP: IGD {} already have a public address ({})",
486 igd_->toString(),
487 igd_->getPublicIp().toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400488 return;
489 }
490 assert(igd_->getProtocol() == NatProtocolType::NAT_PMP);
491
492 int err = sendpublicaddressrequest(&natpmpHdl_);
493
494 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400495 if (logger_) logger_->error("NAT-PMP: send public address request on IGD {} failed with error: {}",
496 igd_->toString(),
497 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400498
499 if (isErrorFatal(err)) {
500 // Fatal error, increment the counter.
501 incrementErrorsCounter(igd_);
502 }
503 return;
504 }
505
506 natpmpresp_t response;
507 err = readResponse(natpmpHdl_, response);
508
509 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400510 if (logger_) logger_->warn("NAT-PMP: Read response on IGD {} failed - {}",
511 igd_->toString(),
512 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400513 return;
514 }
515
516 if (response.type != NATPMP_RESPTYPE_PUBLICADDRESS) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400517 if (logger_) logger_->error("NAT-PMP: Unexpected response type ({:d}) for public address request from IGD {}.",
518 response.type,
519 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400520 return;
521 }
522
523 IpAddr publicAddr(response.pnu.publicaddress.addr);
524
525 if (not publicAddr) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400526 if (logger_) logger_->error("NAT-PMP: IGD {} returned an invalid public address {}",
527 igd_->toString(),
528 publicAddr.toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400529 }
530
531 // Update.
532 igd_->setPublicIp(publicAddr);
533 igd_->setValid(true);
534
Adrien Béraud17765bd2023-08-22 21:02:38 -0400535 if (logger_) logger_->debug("NAT-PMP: Setting IGD {} public address to {}",
536 igd_->toString(),
537 igd_->getPublicIp().toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400538}
539
540void
541NatPmp::removeAllMappings()
542{
Adrien Béraud370257c2023-08-15 20:53:09 -0400543 //CHECK_VALID_THREAD();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400544
Adrien Béraud17765bd2023-08-22 21:02:38 -0400545 if (logger_) logger_->warn("NAT-PMP: Send request to close all existing mappings to IGD {}",
546 igd_->toString().c_str());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400547
548 int err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_TCP, 0, 0, 0);
549 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400550 if (logger_) logger_->warn("NAT-PMP: Send close all TCP mappings request failed with error {}",
551 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400552 }
553 err = sendnewportmappingrequest(&natpmpHdl_, NATPMP_PROTOCOL_UDP, 0, 0, 0);
554 if (err < 0) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400555 if (logger_) logger_->warn("NAT-PMP: Send close all UDP mappings request failed with error {}",
556 getNatPmpErrorStr(err));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400557 }
558}
559
560const char*
561NatPmp::getNatPmpErrorStr(int errorCode) const
562{
563#ifdef ENABLE_STRNATPMPERR
564 return strnatpmperr(errorCode);
565#else
566 switch (errorCode) {
567 case NATPMP_ERR_INVALIDARGS:
568 return "INVALIDARGS";
569 break;
570 case NATPMP_ERR_SOCKETERROR:
571 return "SOCKETERROR";
572 break;
573 case NATPMP_ERR_CANNOTGETGATEWAY:
574 return "CANNOTGETGATEWAY";
575 break;
576 case NATPMP_ERR_CLOSEERR:
577 return "CLOSEERR";
578 break;
579 case NATPMP_ERR_RECVFROM:
580 return "RECVFROM";
581 break;
582 case NATPMP_ERR_NOPENDINGREQ:
583 return "NOPENDINGREQ";
584 break;
585 case NATPMP_ERR_NOGATEWAYSUPPORT:
586 return "NOGATEWAYSUPPORT";
587 break;
588 case NATPMP_ERR_CONNECTERR:
589 return "CONNECTERR";
590 break;
591 case NATPMP_ERR_WRONGPACKETSOURCE:
592 return "WRONGPACKETSOURCE";
593 break;
594 case NATPMP_ERR_SENDERR:
595 return "SENDERR";
596 break;
597 case NATPMP_ERR_FCNTLERROR:
598 return "FCNTLERROR";
599 break;
600 case NATPMP_ERR_GETTIMEOFDAYERR:
601 return "GETTIMEOFDAYERR";
602 break;
603 case NATPMP_ERR_UNSUPPORTEDVERSION:
604 return "UNSUPPORTEDVERSION";
605 break;
606 case NATPMP_ERR_UNSUPPORTEDOPCODE:
607 return "UNSUPPORTEDOPCODE";
608 break;
609 case NATPMP_ERR_UNDEFINEDERROR:
610 return "UNDEFINEDERROR";
611 break;
612 case NATPMP_ERR_NOTAUTHORIZED:
613 return "NOTAUTHORIZED";
614 break;
615 case NATPMP_ERR_NETWORKFAILURE:
616 return "NETWORKFAILURE";
617 break;
618 case NATPMP_ERR_OUTOFRESOURCES:
619 return "OUTOFRESOURCES";
620 break;
621 case NATPMP_TRYAGAIN:
622 return "TRYAGAIN";
623 break;
624 default:
625 return "UNKNOWNERR";
626 break;
627 }
628#endif
629}
630
631bool
632NatPmp::isErrorFatal(int error)
633{
634 switch (error) {
635 case NATPMP_ERR_INVALIDARGS:
636 case NATPMP_ERR_SOCKETERROR:
637 case NATPMP_ERR_CANNOTGETGATEWAY:
638 case NATPMP_ERR_CLOSEERR:
639 case NATPMP_ERR_RECVFROM:
640 case NATPMP_ERR_NOGATEWAYSUPPORT:
641 case NATPMP_ERR_CONNECTERR:
642 case NATPMP_ERR_SENDERR:
643 case NATPMP_ERR_UNDEFINEDERROR:
644 case NATPMP_ERR_UNSUPPORTEDVERSION:
645 case NATPMP_ERR_UNSUPPORTEDOPCODE:
646 case NATPMP_ERR_NOTAUTHORIZED:
647 case NATPMP_ERR_NETWORKFAILURE:
648 case NATPMP_ERR_OUTOFRESOURCES:
649 return true;
650 default:
651 return false;
652 }
653}
654
655bool
656NatPmp::validIgdInstance(const std::shared_ptr<IGD>& igdIn)
657{
658 if (igd_.get() != igdIn.get()) {
Adrien Béraud17765bd2023-08-22 21:02:38 -0400659 if (logger_) logger_->error("NAT-PMP: IGD ({}) does not match local instance ({})",
660 igdIn->toString(),
661 igd_->toString());
Adrien Béraud612b55b2023-05-29 10:42:04 -0400662 return false;
663 }
664
665 return true;
666}
667
668void
669NatPmp::processIgdUpdate(UpnpIgdEvent event)
670{
671 if (igd_->isValid()) {
672 // Remove all current mappings if any.
673 removeAllMappings();
674 }
675
676 if (observer_ == nullptr)
677 return;
678 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400679 ioContext->post([obs = observer_, igd = igd_, event] { obs->onIgdUpdated(igd, event); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400680}
681
682void
683NatPmp::processMappingAdded(const Mapping& map)
684{
685 if (observer_ == nullptr)
686 return;
687
688 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400689 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingAdded(igd, map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400690}
691
692void
693NatPmp::processMappingRequestFailed(const Mapping& map)
694{
695 if (observer_ == nullptr)
696 return;
697
698 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400699 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingRequestFailed(map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400700}
701
702void
703NatPmp::processMappingRenewed(const Mapping& map)
704{
705 if (observer_ == nullptr)
706 return;
707
708 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400709 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingRenewed(igd, map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400710}
711
712void
713NatPmp::processMappingRemoved(const Mapping& map)
714{
715 if (observer_ == nullptr)
716 return;
717
718 // Process the response on the context thread.
Adrien Béraud370257c2023-08-15 20:53:09 -0400719 ioContext->post([obs = observer_, igd = igd_, map] { obs->onMappingRemoved(igd, map); });
Adrien Béraud612b55b2023-05-29 10:42:04 -0400720}
721
722} // namespace upnp
Sébastien Blin464bdff2023-07-19 08:02:53 -0400723} // namespace dhtnet
Adrien Béraud612b55b2023-05-29 10:42:04 -0400724
725#endif //-- #if HAVE_LIBNATPMP