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