Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/upnp/upnp_miniupnp.cpp
10277 views
1
/**************************************************************************/
2
/* upnp_miniupnp.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#ifndef WEB_ENABLED
32
33
#include "upnp_miniupnp.h"
34
35
#include "upnp_device_miniupnp.h"
36
37
#include <miniupnpc/miniwget.h>
38
#include <miniupnpc/upnpcommands.h>
39
40
#include <cstdlib>
41
42
void UPNPMiniUPNP::make_default() {
43
UPNP::_create = UPNPMiniUPNP::_create;
44
}
45
46
bool UPNPMiniUPNP::is_common_device(const String &dev) const {
47
return dev.is_empty() ||
48
dev.contains("InternetGatewayDevice") ||
49
dev.contains("WANIPConnection") ||
50
dev.contains("WANPPPConnection") ||
51
dev.contains("rootdevice");
52
}
53
54
int UPNPMiniUPNP::discover(int timeout, int ttl, const String &device_filter) {
55
ERR_FAIL_COND_V_MSG(timeout < 0, UPNP_RESULT_INVALID_PARAM, "The response's wait time can't be negative.");
56
ERR_FAIL_COND_V_MSG(ttl < 0 || ttl > 255, UPNP_RESULT_INVALID_PARAM, "The time-to-live must be set between 0 and 255 (inclusive).");
57
58
devices.clear();
59
60
int error = 0;
61
struct UPNPDev *devlist;
62
63
CharString cs = discover_multicast_if.utf8();
64
const char *m_if = cs.length() ? cs.get_data() : nullptr;
65
if (is_common_device(device_filter)) {
66
devlist = upnpDiscover(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error);
67
} else {
68
devlist = upnpDiscoverAll(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error);
69
}
70
71
if (error != UPNPDISCOVER_SUCCESS) {
72
switch (error) {
73
case UPNPDISCOVER_SOCKET_ERROR:
74
return UPNP_RESULT_SOCKET_ERROR;
75
case UPNPDISCOVER_MEMORY_ERROR:
76
return UPNP_RESULT_MEM_ALLOC_ERROR;
77
default:
78
return UPNP_RESULT_UNKNOWN_ERROR;
79
}
80
}
81
82
if (!devlist) {
83
return UPNP_RESULT_NO_DEVICES;
84
}
85
86
struct UPNPDev *dev = devlist;
87
88
while (dev) {
89
if (device_filter.is_empty() || strstr(dev->st, device_filter.utf8().get_data())) {
90
add_device_to_list(dev, devlist);
91
}
92
93
dev = dev->pNext;
94
}
95
96
freeUPNPDevlist(devlist);
97
98
return UPNP_RESULT_SUCCESS;
99
}
100
101
void UPNPMiniUPNP::add_device_to_list(UPNPDev *dev, UPNPDev *devlist) {
102
Ref<UPNPDeviceMiniUPNP> new_device;
103
new_device.instantiate();
104
105
new_device->set_description_url(dev->descURL);
106
new_device->set_service_type(dev->st);
107
108
parse_igd(new_device, devlist);
109
110
devices.push_back(new_device);
111
}
112
113
char *UPNPMiniUPNP::load_description(const String &url, int *size, int *status_code) const {
114
return (char *)miniwget(url.utf8().get_data(), size, 0, status_code);
115
}
116
117
void UPNPMiniUPNP::parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist) {
118
int size = 0;
119
int status_code = -1;
120
char *xml = load_description(dev->get_description_url(), &size, &status_code);
121
122
if (status_code != 200) {
123
dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_ERROR);
124
return;
125
}
126
127
if (!xml || size < 1) {
128
dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_EMPTY);
129
return;
130
}
131
132
struct UPNPUrls urls = {};
133
struct IGDdatas data;
134
135
parserootdesc(xml, size, &data);
136
free(xml);
137
xml = nullptr;
138
139
GetUPNPUrls(&urls, &data, dev->get_description_url().utf8().get_data(), 0);
140
141
char addr[16];
142
#if MINIUPNPC_API_VERSION >= 18
143
int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16, nullptr, 0);
144
#else
145
int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16);
146
#endif
147
148
if (i != 1) {
149
FreeUPNPUrls(&urls);
150
151
switch (i) {
152
case 0:
153
dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_IGD);
154
return;
155
case 2:
156
dev->set_igd_status(UPNPDevice::IGD_STATUS_DISCONNECTED);
157
return;
158
case 3:
159
dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_DEVICE);
160
return;
161
default:
162
dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_ERROR);
163
return;
164
}
165
}
166
167
if (urls.controlURL[0] == '\0') {
168
FreeUPNPUrls(&urls);
169
dev->set_igd_status(UPNPDevice::IGD_STATUS_INVALID_CONTROL);
170
return;
171
}
172
173
dev->set_igd_control_url(urls.controlURL);
174
dev->set_igd_service_type(data.first.servicetype);
175
dev->set_igd_our_addr(addr);
176
dev->set_igd_status(UPNPDevice::IGD_STATUS_OK);
177
178
FreeUPNPUrls(&urls);
179
}
180
181
int UPNPMiniUPNP::upnp_result(int in) {
182
switch (in) {
183
case UPNPCOMMAND_SUCCESS:
184
return UPNP_RESULT_SUCCESS;
185
case UPNPCOMMAND_UNKNOWN_ERROR:
186
return UPNP_RESULT_UNKNOWN_ERROR;
187
case UPNPCOMMAND_INVALID_ARGS:
188
return UPNP_RESULT_INVALID_ARGS;
189
case UPNPCOMMAND_HTTP_ERROR:
190
return UPNP_RESULT_HTTP_ERROR;
191
case UPNPCOMMAND_INVALID_RESPONSE:
192
return UPNP_RESULT_INVALID_RESPONSE;
193
case UPNPCOMMAND_MEM_ALLOC_ERROR:
194
return UPNP_RESULT_MEM_ALLOC_ERROR;
195
196
case 402:
197
return UPNP_RESULT_INVALID_ARGS;
198
case 403:
199
return UPNP_RESULT_NOT_AUTHORIZED;
200
case 501:
201
return UPNP_RESULT_ACTION_FAILED;
202
case 606:
203
return UPNP_RESULT_NOT_AUTHORIZED;
204
case 714:
205
return UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY;
206
case 715:
207
return UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED;
208
case 716:
209
return UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED;
210
case 718:
211
return UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING;
212
case 724:
213
return UPNP_RESULT_SAME_PORT_VALUES_REQUIRED;
214
case 725:
215
return UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED;
216
case 726:
217
return UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD;
218
case 727:
219
return UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD;
220
case 728:
221
return UPNP_RESULT_NO_PORT_MAPS_AVAILABLE;
222
case 729:
223
return UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM;
224
case 732:
225
return UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED;
226
case 733:
227
return UPNP_RESULT_INCONSISTENT_PARAMETERS;
228
}
229
230
return UPNP_RESULT_UNKNOWN_ERROR;
231
}
232
233
int UPNPMiniUPNP::get_device_count() const {
234
return devices.size();
235
}
236
237
Ref<UPNPDevice> UPNPMiniUPNP::get_device(int index) const {
238
ERR_FAIL_INDEX_V(index, devices.size(), nullptr);
239
240
return devices.get(index);
241
}
242
243
void UPNPMiniUPNP::add_device(Ref<UPNPDevice> device) {
244
ERR_FAIL_COND(device.is_null());
245
246
devices.push_back(device);
247
}
248
249
void UPNPMiniUPNP::set_device(int index, Ref<UPNPDevice> device) {
250
ERR_FAIL_INDEX(index, devices.size());
251
ERR_FAIL_COND(device.is_null());
252
253
devices.set(index, device);
254
}
255
256
void UPNPMiniUPNP::remove_device(int index) {
257
ERR_FAIL_INDEX(index, devices.size());
258
259
devices.remove_at(index);
260
}
261
262
void UPNPMiniUPNP::clear_devices() {
263
devices.clear();
264
}
265
266
Ref<UPNPDevice> UPNPMiniUPNP::get_gateway() const {
267
ERR_FAIL_COND_V_MSG(devices.is_empty(), nullptr, "Couldn't find any UPNPDevices.");
268
269
for (int i = 0; i < devices.size(); i++) {
270
Ref<UPNPDevice> dev = get_device(i);
271
272
if (dev.is_valid() && dev->is_valid_gateway()) {
273
return dev;
274
}
275
}
276
277
return nullptr;
278
}
279
280
void UPNPMiniUPNP::set_discover_multicast_if(const String &m_if) {
281
discover_multicast_if = m_if;
282
}
283
284
String UPNPMiniUPNP::get_discover_multicast_if() const {
285
return discover_multicast_if;
286
}
287
288
void UPNPMiniUPNP::set_discover_local_port(int port) {
289
discover_local_port = port;
290
}
291
292
int UPNPMiniUPNP::get_discover_local_port() const {
293
return discover_local_port;
294
}
295
296
void UPNPMiniUPNP::set_discover_ipv6(bool ipv6) {
297
discover_ipv6 = ipv6;
298
}
299
300
bool UPNPMiniUPNP::is_discover_ipv6() const {
301
return discover_ipv6;
302
}
303
304
String UPNPMiniUPNP::query_external_address() const {
305
Ref<UPNPDevice> dev = get_gateway();
306
307
if (dev.is_null()) {
308
return "";
309
}
310
311
return dev->query_external_address();
312
}
313
314
int UPNPMiniUPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const {
315
Ref<UPNPDevice> dev = get_gateway();
316
317
if (dev.is_null()) {
318
return UPNP_RESULT_NO_GATEWAY;
319
}
320
321
return dev->add_port_mapping(port, port_internal, desc, proto, duration);
322
}
323
324
int UPNPMiniUPNP::delete_port_mapping(int port, String proto) const {
325
Ref<UPNPDevice> dev = get_gateway();
326
327
if (dev.is_null()) {
328
return UPNP_RESULT_NO_GATEWAY;
329
}
330
331
return dev->delete_port_mapping(port, proto);
332
}
333
334
#endif // WEB_ENABLED
335
336