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