blob: 7c5939c63bbc37990c12a621ddcb28efcdcc1740 [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
25#ifdef _WIN32
26#define UPNP_USE_MSVCPP
27#define UPNP_STATIC_LIB
28#endif
29
30#include "../upnp_protocol.h"
31#include "../igd.h"
32#include "upnp_igd.h"
33
34#include "logger.h"
35#include "connectivity/ip_utils.h"
36#include "noncopyable.h"
37#include "compiler_intrinsics.h"
38
39#include <upnp/upnp.h>
40#include <upnp/upnptools.h>
41
42#ifdef _WIN32
43#include <windows.h>
44#include <wincrypt.h>
45#endif
46
47#include <atomic>
48#include <thread>
49#include <list>
50#include <map>
51#include <set>
52#include <string>
53#include <memory>
54#include <future>
55
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040056namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040057class IpAddr;
58}
59
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040060namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040061namespace upnp {
62
63class PUPnP : public UPnPProtocol
64{
65public:
66 using XMLDocument = std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&>;
67
68 enum class CtrlAction {
69 UNKNOWN,
70 ADD_PORT_MAPPING,
71 DELETE_PORT_MAPPING,
72 GET_GENERIC_PORT_MAPPING_ENTRY,
73 GET_STATUS_INFO,
74 GET_EXTERNAL_IP_ADDRESS
75 };
76
77 PUPnP();
78 ~PUPnP();
79
80 // Set the observer
81 void setObserver(UpnpMappingObserver* obs) override;
82
83 // Returns the protocol type.
84 NatProtocolType getProtocol() const override { return NatProtocolType::PUPNP; }
85
86 // Get protocol type as string.
87 char const* getProtocolName() const override { return "PUPNP"; }
88
89 // Notifies a change in network.
90 void clearIgds() override;
91
92 // Sends out async search for IGD.
93 void searchForIgd() override;
94
95 // Get the IGD list.
96 std::list<std::shared_ptr<IGD>> getIgdList() const override;
97
98 // Return true if the it's fully setup.
99 bool isReady() const override;
100
101 // Get from the IGD the list of already allocated mappings if any.
102 std::map<Mapping::key_t, Mapping> getMappingsListByDescr(
103 const std::shared_ptr<IGD>& igd, const std::string& descr) const override;
104
105 // Request a new mapping.
106 void requestMappingAdd(const Mapping& mapping) override;
107
108 // Renew an allocated mapping.
109 // Not implemented. Currently, UPNP allocations do not have expiration time.
110 void requestMappingRenew([[maybe_unused]] const Mapping& mapping) override { assert(false); };
111
112 // Removes a mapping.
113 void requestMappingRemove(const Mapping& igdMapping) override;
114
115 // Get the host (local) address.
116 const IpAddr getHostAddress() const override;
117
118 // Terminate the instance.
119 void terminate() override;
120
121private:
122 NON_COPYABLE(PUPnP);
123
124 // Helpers to run tasks on PUPNP private execution queue.
125 ScheduledExecutor* getPUPnPScheduler() { return &pupnpScheduler_; }
126 template<typename Callback>
127 void runOnPUPnPQueue(Callback&& cb)
128 {
129 pupnpScheduler_.run([cb = std::forward<Callback>(cb)]() mutable { cb(); });
130 }
131
132 // Helper to run tasks on UPNP context execution queue.
133 ScheduledExecutor* getUpnContextScheduler() { return UpnpThreadUtil::getScheduler(); }
134
135 void terminate(std::condition_variable& cv);
136
137 // Init lib-upnp
138 void initUpnpLib();
139
140 // Return true if running.
141 bool isRunning() const;
142
143 // Register the client
144 void registerClient();
145
146 // Start search for UPNP devices
147 void searchForDevices();
148
149 // Return true if it has at least one valid IGD.
150 bool hasValidIgd() const;
151
152 // Update the host (local) address.
153 void updateHostAddress();
154
155 // Check the host (local) address.
156 // Returns true if the address is valid.
157 bool hasValidHostAddress();
158
159 // Delete mappings matching the description
160 void deleteMappingsByDescription(const std::shared_ptr<IGD>& igd,
161 const std::string& description);
162
163 // Search for the IGD in the local list of known IGDs.
164 std::shared_ptr<UPnPIGD> findMatchingIgd(const std::string& ctrlURL) const;
165
166 // Process the reception of an add mapping action answer.
167 void processAddMapAction(const Mapping& map);
168
169 // Process the a mapping request failure.
170 void processRequestMappingFailure(const Mapping& map);
171
172 // Process the reception of a remove mapping action answer.
173 void processRemoveMapAction(const Mapping& map);
174
175 // Increment IGD errors counter.
176 void incrementErrorsCounter(const std::shared_ptr<IGD>& igd);
177
178 // Download XML document.
179 void downLoadIgdDescription(const std::string& url);
180
181 // Validate IGD from the xml document received from the router.
182 bool validateIgd(const std::string& location, IXML_Document* doc_container_ptr);
183
184 // Returns control point action callback based on xml node.
185 static CtrlAction getAction(const char* xmlNode);
186
187 // Control point callback.
188 static int ctrlPtCallback(Upnp_EventType event_type, const void* event, void* user_data);
189#if UPNP_VERSION < 10800
190 static inline int ctrlPtCallback(Upnp_EventType event_type, void* event, void* user_data)
191 {
192 return ctrlPtCallback(event_type, (const void*) event, user_data);
193 };
194#endif
195 // Process IGD responses.
196 void processDiscoverySearchResult(const std::string& deviceId,
197 const std::string& igdUrl,
198 const IpAddr& dstAddr);
199 void processDiscoveryAdvertisementByebye(const std::string& deviceId);
200 void processDiscoverySubscriptionExpired(Upnp_EventType event_type,
201 const std::string& eventSubUrl);
202
203 // Callback event handler function for the UPnP client (control point).
204 int handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event);
205
206 // Subscription event callback.
207 static int subEventCallback(Upnp_EventType event_type, const void* event, void* user_data);
208#if UPNP_VERSION < 10800
209 static inline int subEventCallback(Upnp_EventType event_type, void* event, void* user_data)
210 {
211 return subEventCallback(event_type, (const void*) event, user_data);
212 };
213#endif
214
215 // Callback subscription event function for handling subscription request.
216 int handleSubscriptionUPnPEvent(Upnp_EventType event_type, const void* event);
217
218 // Parses the IGD candidate.
219 std::unique_ptr<UPnPIGD> parseIgd(IXML_Document* doc, std::string locationUrl);
220
221 // These functions directly create UPnP actions and make synchronous UPnP
222 // control point calls. Must be run on the PUPNP internal execution queue.
223 bool actionIsIgdConnected(const UPnPIGD& igd);
224 IpAddr actionGetExternalIP(const UPnPIGD& igd);
225 bool actionAddPortMapping(const Mapping& mapping);
226 bool actionDeletePortMapping(const Mapping& mapping);
227
228 // Event type to string
229 static const char* eventTypeToString(Upnp_EventType eventType);
230
231 std::weak_ptr<PUPnP> weak() { return std::static_pointer_cast<PUPnP>(shared_from_this()); }
232
233 // Execution queue to run lib upnp actions
234 ScheduledExecutor pupnpScheduler_ {"pupnp"};
235
236 // Initialization status.
237 std::atomic_bool initialized_ {false};
238 // Client registration status.
239 std::atomic_bool clientRegistered_ {false};
240
241 std::shared_ptr<Task> searchForIgdTimer_ {};
242 unsigned int igdSearchCounter_ {0};
243
244 // List of discovered IGDs.
245 std::set<std::string> discoveredIgdList_;
246
247 // Control point handle.
248 UpnpClient_Handle ctrlptHandle_ {-1};
249
250 // Observer to report the results.
251 UpnpMappingObserver* observer_ {nullptr};
252
253 // List of valid IGDs.
254 std::list<std::shared_ptr<IGD>> validIgdList_;
255
256 // Current host address.
257 IpAddr hostAddress_ {};
258
259 // Calls from other threads that does not need synchronous access are
260 // rescheduled on the UPNP private queue. This will avoid the need to
261 // protect most of the data members of this class.
262 // For some internal members (namely the validIgdList and the hostAddress)
263 // that need to be synchronously accessed, are protected by this mutex.
264 mutable std::mutex pupnpMutex_;
265
266 // Shutdown synchronization
267 bool shutdownComplete_ {false};
268};
269
270} // namespace upnp
271} // namespace jami