blob: 3f24bca3b2ac85545cca65cc0413e5c19aa2dab5 [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#pragma once
18
Morteza Namvar5f639522023-07-04 17:08:58 -040019/*#include "upnp_protocol.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040020#if HAVE_LIBNATPMP
21#include "protocol/natpmp/nat_pmp.h"
22#endif
23#if HAVE_LIBUPNP
24#include "protocol/pupnp/pupnp.h"
25#endif
Morteza Namvar5f639522023-07-04 17:08:58 -040026#include "igd.h"*/
Adrien Béraud612b55b2023-05-29 10:42:04 -040027
Adrien Bérauddbb06862023-07-08 09:18:39 -040028#include "../ip_utils.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040029
Adrien Bérauddbb06862023-07-08 09:18:39 -040030#include "mapping.h"
Morteza Namvar5f639522023-07-04 17:08:58 -040031
Adrien Béraud612b55b2023-05-29 10:42:04 -040032#include <opendht/rng.h>
Adrien Béraud25c30c42023-07-05 13:46:54 -040033#include <opendht/logger.h>
Adrien Béraud612b55b2023-05-29 10:42:04 -040034#include <asio/steady_timer.hpp>
35
36#include <set>
37#include <map>
38#include <mutex>
39#include <memory>
40#include <string>
41#include <chrono>
42#include <random>
43#include <atomic>
Morteza Namvar5f639522023-07-04 17:08:58 -040044#include <condition_variable>
Adrien Béraud612b55b2023-05-29 10:42:04 -040045
Morteza Namvar5f639522023-07-04 17:08:58 -040046#include <cstdlib>
Adrien Béraud612b55b2023-05-29 10:42:04 -040047
48using random_device = dht::crypto::random_device;
49
50using IgdFoundCallback = std::function<void()>;
51
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040052namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040053class IpAddr;
54}
55
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040056namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040057namespace upnp {
58
Morteza Namvar5f639522023-07-04 17:08:58 -040059class UPnPProtocol;
60class IGD;
61
62enum class UpnpIgdEvent { ADDED, REMOVED, INVALID_STATE };
63
64// Interface used to report mapping event from the protocol implementations.
65// This interface is meant to be implemented only by UPnPConext class. Sincce
66// this class is a singleton, it's assumed that it out-lives the protocol
67// implementations. In other words, the observer is always assumed to point to a
68// valid instance.
69class UpnpMappingObserver
70{
71public:
72 UpnpMappingObserver() {};
73 virtual ~UpnpMappingObserver() {};
74
75 virtual void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) = 0;
76 virtual void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
77 virtual void onMappingRequestFailed(const Mapping& map) = 0;
78#if HAVE_LIBNATPMP
79 virtual void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
80#endif
81 virtual void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
82};
83
Adrien Béraud370257c2023-08-15 20:53:09 -040084class UPnPContext : public UpnpMappingObserver
Adrien Béraud612b55b2023-05-29 10:42:04 -040085{
86private:
87 struct MappingStatus
88 {
89 int openCount_ {0};
90 int readyCount_ {0};
91 int pendingCount_ {0};
92 int inProgressCount_ {0};
93 int failedCount_ {0};
94
95 void reset()
96 {
97 openCount_ = 0;
98 readyCount_ = 0;
99 pendingCount_ = 0;
100 inProgressCount_ = 0;
101 failedCount_ = 0;
102 };
103 int sum() { return openCount_ + pendingCount_ + inProgressCount_ + failedCount_; }
104 };
105
106public:
Sébastien Blin55abf072023-07-19 10:21:21 -0400107 UPnPContext(const std::shared_ptr<asio::io_context>& ctx, const std::shared_ptr<dht::log::Logger>& logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400108 ~UPnPContext();
109
Adrien Béraudb04fbd72023-08-17 19:56:11 -0400110 std::shared_ptr<asio::io_context> createIoContext(const std::shared_ptr<asio::io_context>& ctx, const std::shared_ptr<dht::log::Logger>& logger);
111
Adrien Béraud612b55b2023-05-29 10:42:04 -0400112 // Terminate the instance.
113 void shutdown();
114
115 // Set the known public address
116 void setPublicAddress(const IpAddr& addr);
117
118 // Check if there is a valid IGD in the IGD list.
119 bool isReady() const;
120
121 // Get external Ip of a chosen IGD.
122 IpAddr getExternalIP() const;
123
124 // Inform the UPnP context that the network status has changed. This clears the list of known
125 void connectivityChanged();
126
127 // Returns a shared pointer of the mapping.
128 Mapping::sharedPtr_t reserveMapping(Mapping& requestedMap);
129
130 // Release an used mapping (make it available for future use).
131 void releaseMapping(const Mapping& map);
132
133 // Register a controller
134 void registerController(void* controller);
135 // Unregister a controller
136 void unregisterController(void* controller);
137
138 // Generate random port numbers
139 static uint16_t generateRandomPort(PortType type, bool mustBeEven = false);
140
Adrien Berauda8731ac2023-08-17 12:19:39 -0400141 template <typename T>
142 inline void dispatch(T&& f) {
143 ctx->dispatch(std::move(f));
144 }
145
Adrien Béraud612b55b2023-05-29 10:42:04 -0400146private:
147 // Initialization
148 void init();
149
150 /**
151 * @brief start the search for IGDs activate the mapping
152 * list update.
153 *
154 */
155 void startUpnp();
156
157 /**
158 * @brief Clear all IGDs and release/delete current mappings
159 *
160 * @param forceRelease If true, also delete mappings with enabled
161 * auto-update feature.
162 *
163 */
164 void stopUpnp(bool forceRelease = false);
165
166 void shutdown(std::condition_variable& cv);
167
168 // Create and register a new mapping.
169 Mapping::sharedPtr_t registerMapping(Mapping& map);
170
171 // Removes the mapping from the list.
Adrien Béraud612b55b2023-05-29 10:42:04 -0400172 void unregisterMapping(const Mapping::sharedPtr_t& map);
173
174 // Perform the request on the provided IGD.
175 void requestMapping(const Mapping::sharedPtr_t& map);
176
177 // Request a mapping remove from the IGD.
178 void requestRemoveMapping(const Mapping::sharedPtr_t& map);
179
180 // Remove all mappings of the given type.
181 void deleteAllMappings(PortType type);
182
Adrien Beraud3bd61c92023-08-17 16:57:37 -0400183 // Update the state and notify the listener
184 void updateMappingState(const Mapping::sharedPtr_t& map,
185 MappingState newState,
186 bool notify = true);
187
Adrien Béraud612b55b2023-05-29 10:42:04 -0400188 // Provision ports.
189 uint16_t getAvailablePortNumber(PortType type);
190
191 // Update preferred IGD
192 void updatePreferredIgd();
193
194 // Get preferred IGD
195 std::shared_ptr<IGD> getPreferredIgd() const;
196
197 // Check and prune the mapping list. Called periodically.
198 void updateMappingList(bool async);
199
200 // Provision (pre-allocate) the requested number of mappings.
201 bool provisionNewMappings(PortType type, int portCount);
202
203 // Close unused mappings.
204 bool deleteUnneededMappings(PortType type, int portCount);
205
206 /**
207 * Prune the mapping list.To avoid competing with allocation
208 * requests, the pruning is performed only if there are no
209 * requests in progress.
210 */
211 void pruneMappingList();
212
213 /**
214 * Check if there are allocated mappings from previous instances,
215 * and try to close them.
216 * Only done for UPNP protocol. NAT-PMP allocations will expire
217 * anyway if not renewed.
218 */
219 void pruneUnMatchedMappings(const std::shared_ptr<IGD>& igd,
220 const std::map<Mapping::key_t, Mapping>& remoteMapList);
221
222 /**
223 * Check the local mapping list against the list returned by the
224 * IGD and remove all mappings which do not have a match.
225 * Only done for UPNP protocol.
226 */
227 void pruneUnTrackedMappings(const std::shared_ptr<IGD>& igd,
228 const std::map<Mapping::key_t, Mapping>& remoteMapList);
229
230 void pruneMappingsWithInvalidIgds(const std::shared_ptr<IGD>& igd);
231
232 /**
233 * @brief Get the mapping list
234 *
235 * @param type transport type (TCP/UDP)
236 * @return a reference on the map
237 * @warning concurrency protection done by the caller
238 */
239 std::map<Mapping::key_t, Mapping::sharedPtr_t>& getMappingList(PortType type);
240
241 // Get the mapping from the key.
242 Mapping::sharedPtr_t getMappingWithKey(Mapping::key_t key);
243
244 // Get the number of mappings per state.
245 void getMappingStatus(PortType type, MappingStatus& status);
246 void getMappingStatus(MappingStatus& status);
247
248#if HAVE_LIBNATPMP
249 void renewAllocations();
250#endif
251
252 // Process requests with pending status.
253 void processPendingRequests(const std::shared_ptr<IGD>& igd);
254
255 // Process mapping with auto-update flag enabled.
256 void processMappingWithAutoUpdate();
257
258 // Implementation of UpnpMappingObserver interface.
259
260 // Callback used to report changes in IGD status.
261 void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) override;
262 // Callback used to report add request status.
263 void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
264 // Callback invoked when a request fails. Reported on failures for both
265 // new requests and renewal requests (if supported by the the protocol).
266 void onMappingRequestFailed(const Mapping& map) override;
267#if HAVE_LIBNATPMP
268 // Callback used to report renew request status.
269 void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
270#endif
271 // Callback used to report remove request status.
272 void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
273
274private:
275 UPnPContext(const UPnPContext&) = delete;
276 UPnPContext(UPnPContext&&) = delete;
277 UPnPContext& operator=(UPnPContext&&) = delete;
278 UPnPContext& operator=(const UPnPContext&) = delete;
279
280 bool started_ {false};
281
282 // The known public address. The external addresses returned by
283 // the IGDs will be checked against this address.
284 IpAddr knownPublicAddress_ {};
285
286 // Set of registered controllers
287 std::set<void*> controllerList_;
288
289 // Map of available protocols.
290 std::map<NatProtocolType, std::shared_ptr<UPnPProtocol>> protocolList_;
291
292 // Port ranges for TCP and UDP (in that order).
293 std::map<PortType, std::pair<uint16_t, uint16_t>> portRange_ {};
294
295 // Min open ports limit
296 int minOpenPortLimit_[2] {4, 8};
297 // Max open ports limit
298 int maxOpenPortLimit_[2] {8, 12};
299
Adrien Béraud370257c2023-08-15 20:53:09 -0400300 std::shared_ptr<asio::io_context> ctx;
301 std::shared_ptr<dht::log::Logger> logger_;
Adrien Berauda8731ac2023-08-17 12:19:39 -0400302 asio::steady_timer mappingListUpdateTimer_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400303
304 // Current preferred IGD. Can be null if there is no valid IGD.
305 std::shared_ptr<IGD> preferredIgd_;
306
307 // This mutex must lock only these two members. All other
308 // members must be accessed only from the UPNP context thread.
309 std::mutex mutable mappingMutex_;
310 // List of mappings.
311 std::map<Mapping::key_t, Mapping::sharedPtr_t> mappingList_[2] {};
312 std::set<std::shared_ptr<IGD>> validIgdList_ {};
313
314 // Shutdown synchronization
315 bool shutdownComplete_ {false};
Adrien Béraudb04fbd72023-08-17 19:56:11 -0400316
317 // Thread
318 std::unique_ptr<std::thread> ioContextRunner_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400319};
320
321} // namespace upnp
Sébastien Blin464bdff2023-07-19 08:02:53 -0400322} // namespace dhtnet