blob: 32fe19a1ed45c9898f8d8c3d38c3164fd4585e04 [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"
Adrien Béraud370257c2023-08-15 20:53:09 -040027#include "ip_utils.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040028
François-Simon Fauteux-Chapleau808db4f2024-04-19 11:39:47 -040029#include <opendht/thread_pool.h>
Adrien Béraud612b55b2023-05-29 10:42:04 -040030#include <upnp/upnp.h>
31#include <upnp/upnptools.h>
32
33#ifdef _WIN32
34#include <windows.h>
35#include <wincrypt.h>
36#endif
37
38#include <atomic>
39#include <thread>
40#include <list>
41#include <map>
42#include <set>
43#include <string>
44#include <memory>
45#include <future>
46
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040047namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040048class IpAddr;
49}
50
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040051namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040052namespace upnp {
53
François-Simon Fauteux-Chapleaufd29c1d2024-05-30 16:48:26 -040054constexpr static unsigned int MAPPING_LEASE_DURATION {604800};
55
Adrien Béraud612b55b2023-05-29 10:42:04 -040056class PUPnP : public UPnPProtocol
57{
58public:
59 using XMLDocument = std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&>;
60
61 enum class CtrlAction {
62 UNKNOWN,
63 ADD_PORT_MAPPING,
64 DELETE_PORT_MAPPING,
65 GET_GENERIC_PORT_MAPPING_ENTRY,
66 GET_STATUS_INFO,
67 GET_EXTERNAL_IP_ADDRESS
68 };
69
Adrien Béraud370257c2023-08-15 20:53:09 -040070 PUPnP(const std::shared_ptr<asio::io_context>& ctx, const std::shared_ptr<dht::log::Logger>& logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -040071 ~PUPnP();
72
73 // Set the observer
74 void setObserver(UpnpMappingObserver* obs) override;
75
76 // Returns the protocol type.
77 NatProtocolType getProtocol() const override { return NatProtocolType::PUPNP; }
78
79 // Get protocol type as string.
80 char const* getProtocolName() const override { return "PUPNP"; }
81
82 // Notifies a change in network.
83 void clearIgds() override;
84
85 // Sends out async search for IGD.
86 void searchForIgd() override;
87
88 // Get the IGD list.
89 std::list<std::shared_ptr<IGD>> getIgdList() const override;
90
91 // Return true if the it's fully setup.
92 bool isReady() const override;
93
94 // Get from the IGD the list of already allocated mappings if any.
95 std::map<Mapping::key_t, Mapping> getMappingsListByDescr(
96 const std::shared_ptr<IGD>& igd, const std::string& descr) const override;
97
François-Simon Fauteux-Chapleau826f0ba2024-05-29 15:22:21 -040098 // Get information about all existing port mappings on the given IGD
99 std::vector<MappingInfo> getMappingsInfo(const std::shared_ptr<IGD>& igd) const override;
100
Adrien Béraud612b55b2023-05-29 10:42:04 -0400101 // Request a new mapping.
102 void requestMappingAdd(const Mapping& mapping) override;
103
104 // Renew an allocated mapping.
François-Simon Fauteux-Chapleaufd29c1d2024-05-30 16:48:26 -0400105 void requestMappingRenew(const Mapping& mapping) override;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400106
107 // Removes a mapping.
108 void requestMappingRemove(const Mapping& igdMapping) override;
109
110 // Get the host (local) address.
111 const IpAddr getHostAddress() const override;
112
113 // Terminate the instance.
114 void terminate() override;
115
116private:
Adrien Béraud370257c2023-08-15 20:53:09 -0400117 PUPnP& operator=(const PUPnP&) = delete;
118 PUPnP(const PUPnP&) = delete;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400119
120 void terminate(std::condition_variable& cv);
121
122 // Init lib-upnp
123 void initUpnpLib();
124
125 // Return true if running.
126 bool isRunning() const;
127
128 // Register the client
129 void registerClient();
130
François-Simon Fauteux-Chapleaud7976982024-04-26 16:06:23 -0400131 // Unregister the client
132 void unregisterClient();
133
Adrien Béraud612b55b2023-05-29 10:42:04 -0400134 // Start search for UPNP devices
135 void searchForDevices();
136
137 // Return true if it has at least one valid IGD.
138 bool hasValidIgd() const;
139
140 // Update the host (local) address.
141 void updateHostAddress();
142
143 // Check the host (local) address.
144 // Returns true if the address is valid.
145 bool hasValidHostAddress();
146
147 // Delete mappings matching the description
148 void deleteMappingsByDescription(const std::shared_ptr<IGD>& igd,
149 const std::string& description);
150
151 // Search for the IGD in the local list of known IGDs.
152 std::shared_ptr<UPnPIGD> findMatchingIgd(const std::string& ctrlURL) const;
153
154 // Process the reception of an add mapping action answer.
155 void processAddMapAction(const Mapping& map);
156
François-Simon Fauteux-Chapleaufd29c1d2024-05-30 16:48:26 -0400157 // Called after a successful mapping renewal
158 void processMappingRenewed(const Mapping& map);
159
Adrien Béraud612b55b2023-05-29 10:42:04 -0400160 // Process the a mapping request failure.
161 void processRequestMappingFailure(const Mapping& map);
162
163 // Process the reception of a remove mapping action answer.
164 void processRemoveMapAction(const Mapping& map);
165
166 // Increment IGD errors counter.
167 void incrementErrorsCounter(const std::shared_ptr<IGD>& igd);
168
169 // Download XML document.
170 void downLoadIgdDescription(const std::string& url);
171
172 // Validate IGD from the xml document received from the router.
173 bool validateIgd(const std::string& location, IXML_Document* doc_container_ptr);
174
175 // Returns control point action callback based on xml node.
176 static CtrlAction getAction(const char* xmlNode);
177
178 // Control point callback.
179 static int ctrlPtCallback(Upnp_EventType event_type, const void* event, void* user_data);
180#if UPNP_VERSION < 10800
181 static inline int ctrlPtCallback(Upnp_EventType event_type, void* event, void* user_data)
182 {
183 return ctrlPtCallback(event_type, (const void*) event, user_data);
184 };
185#endif
186 // Process IGD responses.
187 void processDiscoverySearchResult(const std::string& deviceId,
188 const std::string& igdUrl,
189 const IpAddr& dstAddr);
190 void processDiscoveryAdvertisementByebye(const std::string& deviceId);
191 void processDiscoverySubscriptionExpired(Upnp_EventType event_type,
192 const std::string& eventSubUrl);
193
194 // Callback event handler function for the UPnP client (control point).
195 int handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event);
196
197 // Subscription event callback.
198 static int subEventCallback(Upnp_EventType event_type, const void* event, void* user_data);
199#if UPNP_VERSION < 10800
200 static inline int subEventCallback(Upnp_EventType event_type, void* event, void* user_data)
201 {
202 return subEventCallback(event_type, (const void*) event, user_data);
203 };
204#endif
205
206 // Callback subscription event function for handling subscription request.
207 int handleSubscriptionUPnPEvent(Upnp_EventType event_type, const void* event);
208
209 // Parses the IGD candidate.
210 std::unique_ptr<UPnPIGD> parseIgd(IXML_Document* doc, std::string locationUrl);
211
212 // These functions directly create UPnP actions and make synchronous UPnP
213 // control point calls. Must be run on the PUPNP internal execution queue.
214 bool actionIsIgdConnected(const UPnPIGD& igd);
215 IpAddr actionGetExternalIP(const UPnPIGD& igd);
216 bool actionAddPortMapping(const Mapping& mapping);
217 bool actionDeletePortMapping(const Mapping& mapping);
218
219 // Event type to string
220 static const char* eventTypeToString(Upnp_EventType eventType);
221
222 std::weak_ptr<PUPnP> weak() { return std::static_pointer_cast<PUPnP>(shared_from_this()); }
223
Adrien Béraud612b55b2023-05-29 10:42:04 -0400224 // Initialization status.
225 std::atomic_bool initialized_ {false};
226 // Client registration status.
227 std::atomic_bool clientRegistered_ {false};
228
Adrien Béraud370257c2023-08-15 20:53:09 -0400229 std::shared_ptr<asio::io_context> ioContext;
230 asio::steady_timer searchForIgdTimer_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400231 unsigned int igdSearchCounter_ {0};
232
233 // List of discovered IGDs.
234 std::set<std::string> discoveredIgdList_;
235
236 // Control point handle.
237 UpnpClient_Handle ctrlptHandle_ {-1};
238
239 // Observer to report the results.
240 UpnpMappingObserver* observer_ {nullptr};
241
242 // List of valid IGDs.
243 std::list<std::shared_ptr<IGD>> validIgdList_;
244
245 // Current host address.
246 IpAddr hostAddress_ {};
247
248 // Calls from other threads that does not need synchronous access are
249 // rescheduled on the UPNP private queue. This will avoid the need to
250 // protect most of the data members of this class.
251 // For some internal members (namely the validIgdList and the hostAddress)
252 // that need to be synchronously accessed, are protected by this mutex.
253 mutable std::mutex pupnpMutex_;
254
255 // Shutdown synchronization
256 bool shutdownComplete_ {false};
Sébastien Blind14fc352023-10-06 15:21:53 -0400257
258 // Count ongoing operations
259 std::mutex ongoingOpsMtx_;
Sébastien Blind14fc352023-10-06 15:21:53 -0400260 int ongoingOps_ {0};
261 bool destroying_ {false};
François-Simon Fauteux-Chapleau808db4f2024-04-19 11:39:47 -0400262 dht::ThreadPool ongoingOpsThreadPool_;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400263};
264
265} // namespace upnp
Sébastien Blin464bdff2023-07-19 08:02:53 -0400266} // namespace dhtnet