blob: 2ac63cd10a86311872fce8a8c25d3ac288d96a4d [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
4 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
5 * Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
6 * Author: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#pragma once
24
Morteza Namvar5f639522023-07-04 17:08:58 -040025/*#include "upnp_protocol.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040026#if HAVE_LIBNATPMP
27#include "protocol/natpmp/nat_pmp.h"
28#endif
29#if HAVE_LIBUPNP
30#include "protocol/pupnp/pupnp.h"
31#endif
Morteza Namvar5f639522023-07-04 17:08:58 -040032#include "igd.h"*/
Adrien Béraud612b55b2023-05-29 10:42:04 -040033
Adrien Bérauddbb06862023-07-08 09:18:39 -040034#include "../ip_utils.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040035
Morteza Namvar5f639522023-07-04 17:08:58 -040036#include "upnp_thread_util.h"
Adrien Bérauddbb06862023-07-08 09:18:39 -040037#include "mapping.h"
Morteza Namvar5f639522023-07-04 17:08:58 -040038
Adrien Béraud612b55b2023-05-29 10:42:04 -040039#include <opendht/rng.h>
Adrien Béraud25c30c42023-07-05 13:46:54 -040040#include <opendht/logger.h>
Adrien Béraud612b55b2023-05-29 10:42:04 -040041#include <asio/steady_timer.hpp>
42
43#include <set>
44#include <map>
45#include <mutex>
46#include <memory>
47#include <string>
48#include <chrono>
49#include <random>
50#include <atomic>
Morteza Namvar5f639522023-07-04 17:08:58 -040051#include <condition_variable>
Adrien Béraud612b55b2023-05-29 10:42:04 -040052
Morteza Namvar5f639522023-07-04 17:08:58 -040053#include <cstdlib>
Adrien Béraud612b55b2023-05-29 10:42:04 -040054
55using random_device = dht::crypto::random_device;
56
57using IgdFoundCallback = std::function<void()>;
58
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040059namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040060class IpAddr;
61}
62
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040063namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040064namespace upnp {
65
Morteza Namvar5f639522023-07-04 17:08:58 -040066class UPnPProtocol;
67class IGD;
68
69enum class UpnpIgdEvent { ADDED, REMOVED, INVALID_STATE };
70
71// Interface used to report mapping event from the protocol implementations.
72// This interface is meant to be implemented only by UPnPConext class. Sincce
73// this class is a singleton, it's assumed that it out-lives the protocol
74// implementations. In other words, the observer is always assumed to point to a
75// valid instance.
76class UpnpMappingObserver
77{
78public:
79 UpnpMappingObserver() {};
80 virtual ~UpnpMappingObserver() {};
81
82 virtual void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) = 0;
83 virtual void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
84 virtual void onMappingRequestFailed(const Mapping& map) = 0;
85#if HAVE_LIBNATPMP
86 virtual void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
87#endif
88 virtual void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) = 0;
89};
90
Adrien Béraud33a03ba2023-06-21 11:35:54 -040091class UPnPContext : public UpnpMappingObserver, protected UpnpThreadUtil
Adrien Béraud612b55b2023-05-29 10:42:04 -040092{
93private:
94 struct MappingStatus
95 {
96 int openCount_ {0};
97 int readyCount_ {0};
98 int pendingCount_ {0};
99 int inProgressCount_ {0};
100 int failedCount_ {0};
101
102 void reset()
103 {
104 openCount_ = 0;
105 readyCount_ = 0;
106 pendingCount_ = 0;
107 inProgressCount_ = 0;
108 failedCount_ = 0;
109 };
110 int sum() { return openCount_ + pendingCount_ + inProgressCount_ + failedCount_; }
111 };
112
113public:
Adrien Béraud25c30c42023-07-05 13:46:54 -0400114 UPnPContext(std::shared_ptr<asio::io_context> ctx, std::shared_ptr<dht::log::Logger> logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400115 ~UPnPContext();
116
117 // Retrieve the UPnPContext singleton.
Adrien Béraud25c30c42023-07-05 13:46:54 -0400118 // static std::shared_ptr<UPnPContext> getUPnPContext();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400119
120 // Terminate the instance.
121 void shutdown();
122
123 // Set the known public address
124 void setPublicAddress(const IpAddr& addr);
125
126 // Check if there is a valid IGD in the IGD list.
127 bool isReady() const;
128
129 // Get external Ip of a chosen IGD.
130 IpAddr getExternalIP() const;
131
132 // Inform the UPnP context that the network status has changed. This clears the list of known
133 void connectivityChanged();
134
135 // Returns a shared pointer of the mapping.
136 Mapping::sharedPtr_t reserveMapping(Mapping& requestedMap);
137
138 // Release an used mapping (make it available for future use).
139 void releaseMapping(const Mapping& map);
140
141 // Register a controller
142 void registerController(void* controller);
143 // Unregister a controller
144 void unregisterController(void* controller);
145
146 // Generate random port numbers
147 static uint16_t generateRandomPort(PortType type, bool mustBeEven = false);
148
149private:
150 // Initialization
151 void init();
152
153 /**
154 * @brief start the search for IGDs activate the mapping
155 * list update.
156 *
157 */
158 void startUpnp();
159
160 /**
161 * @brief Clear all IGDs and release/delete current mappings
162 *
163 * @param forceRelease If true, also delete mappings with enabled
164 * auto-update feature.
165 *
166 */
167 void stopUpnp(bool forceRelease = false);
168
169 void shutdown(std::condition_variable& cv);
170
171 // Create and register a new mapping.
172 Mapping::sharedPtr_t registerMapping(Mapping& map);
173
174 // Removes the mapping from the list.
175 std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator unregisterMapping(
176 std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator it);
177 void unregisterMapping(const Mapping::sharedPtr_t& map);
178
179 // Perform the request on the provided IGD.
180 void requestMapping(const Mapping::sharedPtr_t& map);
181
182 // Request a mapping remove from the IGD.
183 void requestRemoveMapping(const Mapping::sharedPtr_t& map);
184
185 // Remove all mappings of the given type.
186 void deleteAllMappings(PortType type);
187
188 // Update the state and notify the listener
189 void updateMappingState(const Mapping::sharedPtr_t& map,
190 MappingState newState,
191 bool notify = true);
192
193 // Provision ports.
194 uint16_t getAvailablePortNumber(PortType type);
195
196 // Update preferred IGD
197 void updatePreferredIgd();
198
199 // Get preferred IGD
200 std::shared_ptr<IGD> getPreferredIgd() const;
201
202 // Check and prune the mapping list. Called periodically.
203 void updateMappingList(bool async);
204
205 // Provision (pre-allocate) the requested number of mappings.
206 bool provisionNewMappings(PortType type, int portCount);
207
208 // Close unused mappings.
209 bool deleteUnneededMappings(PortType type, int portCount);
210
211 /**
212 * Prune the mapping list.To avoid competing with allocation
213 * requests, the pruning is performed only if there are no
214 * requests in progress.
215 */
216 void pruneMappingList();
217
218 /**
219 * Check if there are allocated mappings from previous instances,
220 * and try to close them.
221 * Only done for UPNP protocol. NAT-PMP allocations will expire
222 * anyway if not renewed.
223 */
224 void pruneUnMatchedMappings(const std::shared_ptr<IGD>& igd,
225 const std::map<Mapping::key_t, Mapping>& remoteMapList);
226
227 /**
228 * Check the local mapping list against the list returned by the
229 * IGD and remove all mappings which do not have a match.
230 * Only done for UPNP protocol.
231 */
232 void pruneUnTrackedMappings(const std::shared_ptr<IGD>& igd,
233 const std::map<Mapping::key_t, Mapping>& remoteMapList);
234
235 void pruneMappingsWithInvalidIgds(const std::shared_ptr<IGD>& igd);
236
237 /**
238 * @brief Get the mapping list
239 *
240 * @param type transport type (TCP/UDP)
241 * @return a reference on the map
242 * @warning concurrency protection done by the caller
243 */
244 std::map<Mapping::key_t, Mapping::sharedPtr_t>& getMappingList(PortType type);
245
246 // Get the mapping from the key.
247 Mapping::sharedPtr_t getMappingWithKey(Mapping::key_t key);
248
249 // Get the number of mappings per state.
250 void getMappingStatus(PortType type, MappingStatus& status);
251 void getMappingStatus(MappingStatus& status);
252
253#if HAVE_LIBNATPMP
254 void renewAllocations();
255#endif
256
257 // Process requests with pending status.
258 void processPendingRequests(const std::shared_ptr<IGD>& igd);
259
260 // Process mapping with auto-update flag enabled.
261 void processMappingWithAutoUpdate();
262
263 // Implementation of UpnpMappingObserver interface.
264
265 // Callback used to report changes in IGD status.
266 void onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) override;
267 // Callback used to report add request status.
268 void onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
269 // Callback invoked when a request fails. Reported on failures for both
270 // new requests and renewal requests (if supported by the the protocol).
271 void onMappingRequestFailed(const Mapping& map) override;
272#if HAVE_LIBNATPMP
273 // Callback used to report renew request status.
274 void onMappingRenewed(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
275#endif
276 // Callback used to report remove request status.
277 void onMappingRemoved(const std::shared_ptr<IGD>& igd, const Mapping& map) override;
278
279private:
280 UPnPContext(const UPnPContext&) = delete;
281 UPnPContext(UPnPContext&&) = delete;
282 UPnPContext& operator=(UPnPContext&&) = delete;
283 UPnPContext& operator=(const UPnPContext&) = delete;
284
285 bool started_ {false};
286
287 // The known public address. The external addresses returned by
288 // the IGDs will be checked against this address.
289 IpAddr knownPublicAddress_ {};
290
291 // Set of registered controllers
292 std::set<void*> controllerList_;
293
294 // Map of available protocols.
295 std::map<NatProtocolType, std::shared_ptr<UPnPProtocol>> protocolList_;
296
297 // Port ranges for TCP and UDP (in that order).
298 std::map<PortType, std::pair<uint16_t, uint16_t>> portRange_ {};
299
300 // Min open ports limit
301 int minOpenPortLimit_[2] {4, 8};
302 // Max open ports limit
303 int maxOpenPortLimit_[2] {8, 12};
304
305 //std::shared_ptr<Task> mappingListUpdateTimer_ {};
306 asio::steady_timer mappingListUpdateTimer_;// {};
307
308 // Current preferred IGD. Can be null if there is no valid IGD.
309 std::shared_ptr<IGD> preferredIgd_;
310
311 // This mutex must lock only these two members. All other
312 // members must be accessed only from the UPNP context thread.
313 std::mutex mutable mappingMutex_;
314 // List of mappings.
315 std::map<Mapping::key_t, Mapping::sharedPtr_t> mappingList_[2] {};
316 std::set<std::shared_ptr<IGD>> validIgdList_ {};
317
318 // Shutdown synchronization
319 bool shutdownComplete_ {false};
320};
321
322} // namespace upnp
323} // namespace jami