diff --git a/SConstruct b/SConstruct
index 3056e03d481..a1d13b9cdbf 100644
--- a/SConstruct
+++ b/SConstruct
@@ -175,6 +175,7 @@ opts.Add(EnumVariable('macports_clang', "Build using clang from MacPorts", 'no',
# Thirdparty libraries
opts.Add(BoolVariable('builtin_bullet', "Use the builtin bullet library", True))
opts.Add(BoolVariable('builtin_enet', "Use the builtin enet library", True))
+opts.Add(BoolVariable('builtin_miniupnpc', "Use the builtin miniupnpc library", True))
opts.Add(BoolVariable('builtin_freetype', "Use the builtin freetype library", True))
opts.Add(BoolVariable('builtin_libogg', "Use the builtin libogg library", True))
opts.Add(BoolVariable('builtin_libpng', "Use the builtin libpng library", True))
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub
new file mode 100644
index 00000000000..cde231867fb
--- /dev/null
+++ b/modules/upnp/SCsub
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_upnp = env_modules.Clone()
+
+# Thirdparty source files
+
+if env['builtin_miniupnpc']:
+ thirdparty_dir = "#thirdparty/miniupnpc/"
+ thirdparty_sources = [
+ "miniupnpc.c",
+ "upnpcommands.c",
+ "miniwget.c",
+ "upnpdev.c",
+ "igd_desc_parse.c",
+ "minissdpc.c",
+ "minisoap.c",
+ "minixml.c",
+ "connecthostport.c",
+ "receivedata.c",
+ "portlistingparse.c",
+ "upnpreplyparse.c",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_upnp.add_source_files(env.modules_sources, thirdparty_sources)
+ env_upnp.Append(CPPPATH=[thirdparty_dir])
+ env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"])
+
+env_upnp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/upnp/config.py b/modules/upnp/config.py
new file mode 100644
index 00000000000..8724ff1a511
--- /dev/null
+++ b/modules/upnp/config.py
@@ -0,0 +1,14 @@
+def can_build(env, platform):
+ return True
+
+def configure(env):
+ pass
+
+def get_doc_classes():
+ return [
+ "UPNP",
+ "UPNPDevice"
+ ]
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml
new file mode 100644
index 00000000000..30be9c836bb
--- /dev/null
+++ b/modules/upnp/doc_classes/UPNP.xml
@@ -0,0 +1,227 @@
+
+
+
+ UPNP network functions.
+
+
+ Provides UPNP functionality to discover [UPNPDevice]s on the local network and execute commands on them, like managing port mappings (port forwarding) and querying the local and remote network IP address. Note that methods on this class are synchronous and block the calling thread.
+
+
+
+
+
+
+
+
+
+
+
+
+ Adds the given [UPNPDevice] to the list of discovered devices.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Adds a mapping to forward the external [code]port[/code] (between 1 and 65535) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any.
+ If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value).
+ The description ([code]desc[/code]) is shown in some router UIs and can be used to point out which application added the mapping, and the lifetime of the mapping can be limited by [code]duration[/code]. However, some routers are incompatible with one or both of these, so use with caution and add fallback logic in case of errors to retry without them if in doubt.
+ See [enum UPNPResult] for possible return values.
+
+
+
+
+
+
+ Clears the list of discovered devices.
+
+
+
+
+
+
+
+
+
+
+ Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. See [enum UPNPResult] for possible return values.
+
+
+
+
+
+
+
+
+
+
+
+
+ Discovers local [UPNPDevice]s. Clears the list of previously discovered devices.
+ Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in miliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing.
+ See [enum UPNPResult] for possible return values.
+
+
+
+
+
+
+
+
+ Returns the [UPNPDevice] at the given [code]index[/code].
+
+
+
+
+
+
+ Returns the number of discovered [UPNPDevice]s.
+
+
+
+
+
+
+ Returns the default gateway. That is the first discovered [UPNPDevice] that is also a valid IGD (InternetGatewayDevice).
+
+
+
+
+
+
+ Returns the external [IP] address of the default gateway (see [method get_gateway]) as string. Returns an empty string on error.
+
+
+
+
+
+
+
+
+ Removes the device at [code]index[/code] from the list of discovered devices.
+
+
+
+
+
+
+
+
+
+
+ Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code].
+
+
+
+
+
+ If [code]true[/code], IPv6 is used for [UPNPDevice] discovery.
+
+
+ If [code]0[/code], the local port to use for discovery is chosen automatically by the system. If [code]1[/code], discovery will be done from the source port 1900 (same as destination port). Otherwise, the value will be used as the port.
+
+
+ Multicast interface to use for discovery. Uses the default multicast interface if empty.
+
+
+
+
+ UPNP command or discovery was successful.
+
+
+ Not authorized to use the command on the [UPNPDevice]. May be returned when the user disabled UPNP on their router.
+
+
+ No port mapping was found for the given port, protocol combination on the given [UPNPDevice].
+
+
+ Inconsistent parameters.
+
+
+ No such entry in array. May be returned if a given port, protocol combination is not found on an [UPNPDevice].
+
+
+ The action failed.
+
+
+ The [UPNPDevice] does not allow wildcard values for the source IP address.
+
+
+ The [UPNPDevice] does not allow wildcard values for the external port.
+
+
+ The [UPNPDevice] does not allow wildcard values for the internal port.
+
+
+ The remote host value must be a wildcard.
+
+
+ The external port value must be a wildcard.
+
+
+ No port maps are available. May also be returned if port mapping functionality is not available.
+
+
+ Conflict with other mechanism. May be returned instead of [code]UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING[/code] if a port mapping conflicts with an existing one.
+
+
+ Conflict with an existing port mapping.
+
+
+ External and internal port values must be the same.
+
+
+ Only permanent leases are supported. Do not use the [code]duration[/code] parameter when adding port mappings.
+
+
+ Invalid gateway.
+
+
+ Invalid port.
+
+
+ Invalid protocol.
+
+
+ Invalid duration.
+
+
+ Invalid arguments.
+
+
+ Invalid response.
+
+
+ Invalid parameter.
+
+
+ HTTP error.
+
+
+ Socket error.
+
+
+ Error allocating memory.
+
+
+ No gateway available. You may need to call [method discover] first, or discovery didn't detect any valid IGDs (InternetGatewayDevices).
+
+
+ No devices available. You may need to call [method discover] first, or discovery didn't detect any valid [UPNPDevice]s.
+
+
+ Unknown error.
+
+
+
diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml
new file mode 100644
index 00000000000..9de8042daf2
--- /dev/null
+++ b/modules/upnp/doc_classes/UPNPDevice.xml
@@ -0,0 +1,109 @@
+
+
+
+ UPNP device.
+
+
+ UPNP device. See [UPNP] for UPNP discovery and utility functions. Provides low-level access to UPNP control commands. Allows to manage port mappings (port forwarding) and to query network information of the device (like local and external IP address and status). Note that methods on this class are synchronous and block the calling thread.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Adds a port mapping to forward the given external port on this [UPNPDevice] for the given protocol to the local machine. See [method UPNP.add_port_mapping].
+
+
+
+
+
+
+
+
+
+
+ Deletes the port mapping identified by the given port and protocol combination on this device. See [method UPNP.delete_port_mapping].
+
+
+
+
+
+
+ Returns [code]true[/code] if this is a valid IGD (InternetGatewayDevice) which potentially supports port forwarding.
+
+
+
+
+
+
+ Returns the external IP address of this [UPNPDevice] or an empty string.
+
+
+
+
+
+ URL to the device description.
+
+
+ IDG control URL.
+
+
+ Address of the local machine in the network connecting it to this [UPNPDevice].
+
+
+ IGD service type.
+
+
+ IGD status. See [enum IGDStatus].
+
+
+ Service type.
+
+
+
+
+ OK.
+
+
+ HTTP error.
+
+
+ Empty HTTP response.
+
+
+ Returned response contained no URLs.
+
+
+ Not a valid IGD.
+
+
+ Disconnected.
+
+
+ Unknown device.
+
+
+ Invalid control.
+
+
+ Memory allocation error.
+
+
+ Unknown error.
+
+
+
diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp
new file mode 100644
index 00000000000..c79155c4d25
--- /dev/null
+++ b/modules/upnp/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "error_macros.h"
+#include "upnp.h"
+#include "upnpdevice.h"
+
+void register_upnp_types() {
+
+ ClassDB::register_class();
+ ClassDB::register_class();
+}
+
+void unregister_upnp_types() {
+}
diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h
new file mode 100644
index 00000000000..2aeb06abc7b
--- /dev/null
+++ b/modules/upnp/register_types.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+void register_upnp_types();
+void unregister_upnp_types();
diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp
new file mode 100644
index 00000000000..32fdfe22f83
--- /dev/null
+++ b/modules/upnp/upnp.cpp
@@ -0,0 +1,401 @@
+/*************************************************************************/
+/* upnp.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "upnp.h"
+#include "miniupnpc/miniwget.h"
+#include "upnpcommands.h"
+#include
+
+bool UPNP::is_common_device(const String &dev) const {
+ return dev.empty() ||
+ dev.find("InternetGatewayDevice") >= 0 ||
+ dev.find("WANIPConnection") >= 0 ||
+ dev.find("WANPPPConnection") >= 0 ||
+ dev.find("rootdevice") >= 0;
+}
+
+int UPNP::discover(int timeout, int ttl, const String &device_filter) {
+ ERR_FAIL_COND_V(timeout < 0, UPNP_RESULT_INVALID_PARAM);
+ ERR_FAIL_COND_V(ttl < 0, UPNP_RESULT_INVALID_PARAM);
+ ERR_FAIL_COND_V(ttl > 255, UPNP_RESULT_INVALID_PARAM);
+
+ devices.clear();
+
+ int error = 0;
+ struct UPNPDev *devlist;
+
+ if (is_common_device(device_filter)) {
+ devlist = upnpDiscover(timeout, discover_multicast_if.utf8().get_data(), NULL, discover_local_port, discover_ipv6, ttl, &error);
+ } else {
+ devlist = upnpDiscoverAll(timeout, discover_multicast_if.utf8().get_data(), NULL, discover_local_port, discover_ipv6, ttl, &error);
+ }
+
+ if (error != UPNPDISCOVER_SUCCESS) {
+ switch (error) {
+ case UPNPDISCOVER_SOCKET_ERROR:
+ return UPNP_RESULT_SOCKET_ERROR;
+ case UPNPDISCOVER_MEMORY_ERROR:
+ return UPNP_RESULT_MEM_ALLOC_ERROR;
+ default:
+ return UPNP_RESULT_UNKNOWN_ERROR;
+ }
+ }
+
+ if (!devlist) {
+ return UPNP_RESULT_NO_DEVICES;
+ }
+
+ struct UPNPDev *dev = devlist;
+
+ while (dev) {
+ if (device_filter.empty() || strstr(dev->st, device_filter.utf8().get_data())) {
+ add_device_to_list(dev, devlist);
+ }
+
+ dev = dev->pNext;
+ }
+
+ freeUPNPDevlist(devlist);
+
+ return UPNP_RESULT_SUCCESS;
+}
+
+void UPNP::add_device_to_list(UPNPDev *dev, UPNPDev *devlist) {
+ Ref new_device;
+ new_device.instance();
+
+ new_device->set_description_url(dev->descURL);
+ new_device->set_service_type(dev->st);
+
+ parse_igd(new_device, devlist);
+
+ devices.push_back(new_device);
+}
+
+char *UPNP::load_description(const String &url, int *size, int *status_code) const {
+ return (char *)miniwget(url.utf8().get_data(), size, 0, status_code);
+}
+
+void UPNP::parse_igd(Ref dev, UPNPDev *devlist) {
+ int size = 0;
+ int status_code = -1;
+ char *xml = load_description(dev->get_description_url(), &size, &status_code);
+
+ if (status_code != 200) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_ERROR);
+ return;
+ }
+
+ if (!xml || size < 1) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_EMPTY);
+ return;
+ }
+
+ struct UPNPUrls *urls = (UPNPUrls *)malloc(sizeof(struct UPNPUrls));
+
+ if (!urls) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_MALLOC_ERROR);
+ return;
+ }
+
+ struct IGDdatas data;
+
+ memset(urls, 0, sizeof(struct UPNPUrls));
+
+ parserootdesc(xml, size, &data);
+ free(xml);
+ xml = 0;
+
+ GetUPNPUrls(urls, &data, dev->get_description_url().utf8().get_data(), 0);
+
+ if (!urls) {
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_URLS);
+ return;
+ }
+
+ char addr[16];
+ int i = UPNP_GetValidIGD(devlist, urls, &data, (char *)&addr, 16);
+
+ if (i != 1) {
+ FreeUPNPUrls(urls);
+
+ switch (i) {
+ case 0:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_IGD);
+ return;
+ case 2:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_DISCONNECTED);
+ return;
+ case 3:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_DEVICE);
+ return;
+ default:
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_ERROR);
+ return;
+ }
+ }
+
+ if (urls->controlURL[0] == '\0') {
+ FreeUPNPUrls(urls);
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_INVALID_CONTROL);
+ return;
+ }
+
+ dev->set_igd_control_url(urls->controlURL);
+ dev->set_igd_service_type(data.first.servicetype);
+ dev->set_igd_our_addr(addr);
+ dev->set_igd_status(UPNPDevice::IGD_STATUS_OK);
+
+ FreeUPNPUrls(urls);
+}
+
+int UPNP::upnp_result(int in) {
+ switch (in) {
+ case UPNPCOMMAND_SUCCESS:
+ return UPNP_RESULT_SUCCESS;
+ case UPNPCOMMAND_UNKNOWN_ERROR:
+ return UPNP_RESULT_UNKNOWN_ERROR;
+ case UPNPCOMMAND_INVALID_ARGS:
+ return UPNP_RESULT_INVALID_ARGS;
+ case UPNPCOMMAND_HTTP_ERROR:
+ return UPNP_RESULT_HTTP_ERROR;
+ case UPNPCOMMAND_INVALID_RESPONSE:
+ return UPNP_RESULT_INVALID_RESPONSE;
+ case UPNPCOMMAND_MEM_ALLOC_ERROR:
+ return UPNP_RESULT_MEM_ALLOC_ERROR;
+
+ case 402:
+ return UPNP_RESULT_INVALID_ARGS;
+ case 403:
+ return UPNP_RESULT_NOT_AUTHORIZED;
+ case 501:
+ return UPNP_RESULT_ACTION_FAILED;
+ case 606:
+ return UPNP_RESULT_NOT_AUTHORIZED;
+ case 714:
+ return UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY;
+ case 715:
+ return UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED;
+ case 716:
+ return UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED;
+ case 718:
+ return UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING;
+ case 724:
+ return UPNP_RESULT_SAME_PORT_VALUES_REQUIRED;
+ case 725:
+ return UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED;
+ case 726:
+ return UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD;
+ case 727:
+ return UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD;
+ case 728:
+ return UPNP_RESULT_NO_PORT_MAPS_AVAILABLE;
+ case 729:
+ return UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM;
+ case 732:
+ return UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED;
+ case 733:
+ return UPNP_RESULT_INCONSISTENT_PARAMETERS;
+ }
+
+ return UPNP_RESULT_UNKNOWN_ERROR;
+}
+
+int UPNP::get_device_count() const {
+ return devices.size();
+}
+
+Ref UPNP::get_device(int index) const {
+ ERR_FAIL_INDEX_V(index, devices.size(), NULL);
+
+ return devices.get(index);
+}
+
+void UPNP::add_device(Ref device) {
+ ERR_FAIL_COND(device == NULL);
+
+ devices.push_back(device);
+}
+
+void UPNP::set_device(int index, Ref device) {
+ ERR_FAIL_INDEX(index, devices.size());
+ ERR_FAIL_COND(device == NULL);
+
+ devices.set(index, device);
+}
+
+void UPNP::remove_device(int index) {
+ ERR_FAIL_INDEX(index, devices.size());
+
+ devices.remove(index);
+}
+
+void UPNP::clear_devices() {
+ devices.clear();
+}
+
+Ref UPNP::get_gateway() const {
+ ERR_FAIL_COND_V(devices.size() < 1, NULL);
+
+ for (int i = 0; i < devices.size(); i++) {
+ Ref dev = get_device(i);
+
+ if (dev != NULL && dev->is_valid_gateway()) {
+ return dev;
+ }
+ }
+
+ return NULL;
+}
+
+void UPNP::set_discover_multicast_if(const String &m_if) {
+ discover_multicast_if = m_if;
+}
+
+String UPNP::get_discover_multicast_if() const {
+ return discover_multicast_if;
+}
+
+void UPNP::set_discover_local_port(int port) {
+ discover_local_port = port;
+}
+
+int UPNP::get_discover_local_port() const {
+ return discover_local_port;
+}
+
+void UPNP::set_discover_ipv6(bool ipv6) {
+ discover_ipv6 = ipv6;
+}
+
+bool UPNP::is_discover_ipv6() const {
+ return discover_ipv6;
+}
+
+String UPNP::query_external_address() const {
+ Ref dev = get_gateway();
+
+ if (dev == NULL) {
+ return "";
+ }
+
+ return dev->query_external_address();
+}
+
+int UPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const {
+ Ref dev = get_gateway();
+
+ if (dev == NULL) {
+ return UPNP_RESULT_NO_GATEWAY;
+ }
+
+ dev->delete_port_mapping(port, proto);
+
+ return dev->add_port_mapping(port, port_internal, desc, proto, duration);
+}
+
+int UPNP::delete_port_mapping(int port, String proto) const {
+ Ref dev = get_gateway();
+
+ if (dev == NULL) {
+ return UPNP_RESULT_NO_GATEWAY;
+ }
+
+ return dev->delete_port_mapping(port, proto);
+}
+
+void UPNP::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_device_count"), &UPNP::get_device_count);
+ ClassDB::bind_method(D_METHOD("get_device", "index"), &UPNP::get_device);
+ ClassDB::bind_method(D_METHOD("add_device", "device"), &UPNP::add_device);
+ ClassDB::bind_method(D_METHOD("set_device", "index", "device"), &UPNP::set_device);
+ ClassDB::bind_method(D_METHOD("remove_device", "index"), &UPNP::remove_device);
+ ClassDB::bind_method(D_METHOD("clear_devices"), &UPNP::clear_devices);
+
+ ClassDB::bind_method(D_METHOD("get_gateway"), &UPNP::get_gateway);
+
+ ClassDB::bind_method(D_METHOD("discover", "timeout", "ttl", "device_filter"), &UPNP::discover, DEFVAL(2000), DEFVAL(2), DEFVAL("InternetGatewayDevice"));
+
+ ClassDB::bind_method(D_METHOD("query_external_address"), &UPNP::query_external_address);
+
+ ClassDB::bind_method(D_METHOD("add_port_mapping", "port", "port_internal", "desc", "proto", "duration"), &UPNP::add_port_mapping, DEFVAL(0), DEFVAL(""), DEFVAL("UDP"), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("delete_port_mapping", "port", "proto"), &UPNP::delete_port_mapping, DEFVAL("UDP"));
+
+ ClassDB::bind_method(D_METHOD("set_discover_multicast_if", "m_if"), &UPNP::set_discover_multicast_if);
+ ClassDB::bind_method(D_METHOD("get_discover_multicast_if"), &UPNP::get_discover_multicast_if);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "discover_multicast_if"), "set_discover_multicast_if", "get_discover_multicast_if");
+
+ ClassDB::bind_method(D_METHOD("set_discover_local_port", "port"), &UPNP::set_discover_local_port);
+ ClassDB::bind_method(D_METHOD("get_discover_local_port"), &UPNP::get_discover_local_port);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "discover_local_port", PROPERTY_HINT_RANGE, "0,65535"), "set_discover_local_port", "get_discover_local_port");
+
+ ClassDB::bind_method(D_METHOD("set_discover_ipv6", "ipv6"), &UPNP::set_discover_ipv6);
+ ClassDB::bind_method(D_METHOD("is_discover_ipv6"), &UPNP::is_discover_ipv6);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "discover_ipv6"), "set_discover_ipv6", "is_discover_ipv6");
+
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SUCCESS);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NOT_AUTHORIZED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_PORT_MAPPING_NOT_FOUND);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INCONSISTENT_PARAMETERS);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_ACTION_FAILED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_PORT_MAPS_AVAILABLE);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SAME_PORT_VALUES_REQUIRED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_GATEWAY);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PORT);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PROTOCOL);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_DURATION);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_ARGS);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_RESPONSE);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PARAM);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_HTTP_ERROR);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_SOCKET_ERROR);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_MEM_ALLOC_ERROR);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_GATEWAY);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_NO_DEVICES);
+ BIND_ENUM_CONSTANT(UPNP_RESULT_UNKNOWN_ERROR);
+}
+
+UPNP::UPNP() {
+ discover_multicast_if = "";
+ discover_local_port = 0;
+ discover_ipv6 = false;
+}
+
+UPNP::~UPNP() {
+}
diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h
new file mode 100644
index 00000000000..fb0c0f30a02
--- /dev/null
+++ b/modules/upnp/upnp.h
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* upnp.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_UPNP_H
+#define GODOT_UPNP_H
+
+#include "miniupnpc/miniupnpc.h"
+#include "upnpdevice.h"
+#include
+
+class UPNP : public Reference {
+
+ GDCLASS(UPNP, Reference);
+
+private:
+ String discover_multicast_if;
+ int discover_local_port;
+ bool discover_ipv6;
+
+ Vector[ > devices;
+
+ bool is_common_device(const String &dev) const;
+ void add_device_to_list(UPNPDev *dev, UPNPDev *devlist);
+ void parse_igd(Ref dev, UPNPDev *devlist);
+ char *load_description(const String &url, int *size, int *status_code) const;
+
+protected:
+ static void _bind_methods();
+
+public:
+ enum UPNPResult {
+
+ UPNP_RESULT_SUCCESS,
+ UPNP_RESULT_NOT_AUTHORIZED,
+ UPNP_RESULT_PORT_MAPPING_NOT_FOUND,
+ UPNP_RESULT_INCONSISTENT_PARAMETERS,
+ UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY,
+ UPNP_RESULT_ACTION_FAILED,
+ UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED,
+ UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED,
+ UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED,
+ UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD,
+ UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD,
+ UPNP_RESULT_NO_PORT_MAPS_AVAILABLE,
+ UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM,
+ UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING,
+ UPNP_RESULT_SAME_PORT_VALUES_REQUIRED,
+ UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED,
+ UPNP_RESULT_INVALID_GATEWAY,
+ UPNP_RESULT_INVALID_PORT,
+ UPNP_RESULT_INVALID_PROTOCOL,
+ UPNP_RESULT_INVALID_DURATION,
+ UPNP_RESULT_INVALID_ARGS,
+ UPNP_RESULT_INVALID_RESPONSE,
+ UPNP_RESULT_INVALID_PARAM,
+ UPNP_RESULT_HTTP_ERROR,
+ UPNP_RESULT_SOCKET_ERROR,
+ UPNP_RESULT_MEM_ALLOC_ERROR,
+ UPNP_RESULT_NO_GATEWAY,
+ UPNP_RESULT_NO_DEVICES,
+ UPNP_RESULT_UNKNOWN_ERROR,
+ };
+
+ static int upnp_result(int in);
+
+ int get_device_count() const;
+ Ref get_device(int index) const;
+ void add_device(Ref device);
+ void set_device(int index, Ref device);
+ void remove_device(int index);
+ void clear_devices();
+
+ Ref get_gateway() const;
+
+ int discover(int timeout = 2000, int ttl = 2, const String &device_filter = "InternetGatewayDevice");
+
+ String query_external_address() const;
+
+ int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const;
+ int delete_port_mapping(int port, String proto = "UDP") const;
+
+ void set_discover_multicast_if(const String &m_if);
+ String get_discover_multicast_if() const;
+
+ void set_discover_local_port(int port);
+ int get_discover_local_port() const;
+
+ void set_discover_ipv6(bool ipv6);
+ bool is_discover_ipv6() const;
+
+ UPNP();
+ ~UPNP();
+};
+
+VARIANT_ENUM_CAST(UPNP::UPNPResult)
+
+#endif // GODOT_UPNP_H
diff --git a/modules/upnp/upnpdevice.cpp b/modules/upnp/upnpdevice.cpp
new file mode 100644
index 00000000000..a5959cf6491
--- /dev/null
+++ b/modules/upnp/upnpdevice.cpp
@@ -0,0 +1,197 @@
+/*************************************************************************/
+/* upnpdevice.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "upnpdevice.h"
+#include "upnp.h"
+#include "upnpcommands.h"
+
+String UPNPDevice::query_external_address() const {
+ ERR_FAIL_COND_V(!is_valid_gateway(), "");
+
+ char addr[16];
+ int i = UPNP_GetExternalIPAddress(
+ igd_control_url.utf8().get_data(),
+ igd_service_type.utf8().get_data(),
+ (char *)&addr);
+
+ ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, "");
+
+ return String(addr);
+}
+
+int UPNPDevice::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const {
+ ERR_FAIL_COND_V(!is_valid_gateway(), UPNP::UPNP_RESULT_INVALID_GATEWAY);
+ ERR_FAIL_COND_V(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT);
+ ERR_FAIL_COND_V(port_internal < 0 || port_internal > 65535, UPNP::UPNP_RESULT_INVALID_PORT); // Needs to allow 0 because 0 signifies "use external port as internal port"
+ ERR_FAIL_COND_V(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL);
+ ERR_FAIL_COND_V(duration < 0, UPNP::UPNP_RESULT_INVALID_DURATION);
+
+ if (port_internal < 1) {
+ port_internal = port;
+ }
+
+ int i = UPNP_AddPortMapping(
+ igd_control_url.utf8().get_data(),
+ igd_service_type.utf8().get_data(),
+ itos(port).utf8().get_data(),
+ itos(port_internal).utf8().get_data(),
+ igd_our_addr.utf8().get_data(),
+ desc.empty() ? 0 : desc.utf8().get_data(),
+ proto.utf8().get_data(),
+ NULL, // Remote host, always NULL as IGDs don't support it
+ duration > 0 ? itos(duration).utf8().get_data() : 0);
+
+ ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i));
+
+ return UPNP::UPNP_RESULT_SUCCESS;
+}
+
+int UPNPDevice::delete_port_mapping(int port, String proto) const {
+ ERR_FAIL_COND_V(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT);
+ ERR_FAIL_COND_V(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL);
+
+ int i = UPNP_DeletePortMapping(
+ igd_control_url.utf8().get_data(),
+ igd_service_type.utf8().get_data(),
+ itos(port).utf8().get_data(),
+ proto.utf8().get_data(),
+ NULL // Remote host, always NULL as IGDs don't support it
+ );
+
+ ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i));
+
+ return UPNP::UPNP_RESULT_SUCCESS;
+}
+
+void UPNPDevice::set_description_url(const String &url) {
+ description_url = url;
+}
+
+String UPNPDevice::get_description_url() const {
+ return description_url;
+}
+
+void UPNPDevice::set_service_type(const String &type) {
+ service_type = type;
+}
+
+String UPNPDevice::get_service_type() const {
+ return service_type;
+}
+
+void UPNPDevice::set_igd_control_url(const String &url) {
+ igd_control_url = url;
+}
+
+String UPNPDevice::get_igd_control_url() const {
+ return igd_control_url;
+}
+
+void UPNPDevice::set_igd_service_type(const String &type) {
+ igd_service_type = type;
+}
+
+String UPNPDevice::get_igd_service_type() const {
+ return igd_service_type;
+}
+
+void UPNPDevice::set_igd_our_addr(const String &addr) {
+ igd_our_addr = addr;
+}
+
+String UPNPDevice::get_igd_our_addr() const {
+ return igd_our_addr;
+}
+
+void UPNPDevice::set_igd_status(IGDStatus status) {
+ igd_status = status;
+}
+
+UPNPDevice::IGDStatus UPNPDevice::get_igd_status() const {
+ return igd_status;
+}
+
+bool UPNPDevice::is_valid_gateway() const {
+ return igd_status == IGD_STATUS_OK;
+}
+
+void UPNPDevice::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_valid_gateway"), &UPNPDevice::is_valid_gateway);
+ ClassDB::bind_method(D_METHOD("query_external_address"), &UPNPDevice::query_external_address);
+ ClassDB::bind_method(D_METHOD("add_port_mapping", "port", "port_internal", "desc", "proto", "duration"), &UPNPDevice::add_port_mapping, DEFVAL(0), DEFVAL(""), DEFVAL("UDP"), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("delete_port_mapping", "port", "proto"), &UPNPDevice::delete_port_mapping, DEFVAL("UDP"));
+
+ ClassDB::bind_method(D_METHOD("set_description_url", "url"), &UPNPDevice::set_description_url);
+ ClassDB::bind_method(D_METHOD("get_description_url"), &UPNPDevice::get_description_url);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "description_url"), "set_description_url", "get_description_url");
+
+ ClassDB::bind_method(D_METHOD("set_service_type", "type"), &UPNPDevice::set_service_type);
+ ClassDB::bind_method(D_METHOD("get_service_type"), &UPNPDevice::get_service_type);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "service_type"), "set_service_type", "get_service_type");
+
+ ClassDB::bind_method(D_METHOD("set_igd_control_url", "url"), &UPNPDevice::set_igd_control_url);
+ ClassDB::bind_method(D_METHOD("get_igd_control_url"), &UPNPDevice::get_igd_control_url);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_control_url"), "set_igd_control_url", "get_igd_control_url");
+
+ ClassDB::bind_method(D_METHOD("set_igd_service_type", "type"), &UPNPDevice::set_igd_service_type);
+ ClassDB::bind_method(D_METHOD("get_igd_service_type"), &UPNPDevice::get_igd_service_type);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_service_type"), "set_igd_service_type", "get_igd_service_type");
+
+ ClassDB::bind_method(D_METHOD("set_igd_our_addr", "addr"), &UPNPDevice::set_igd_our_addr);
+ ClassDB::bind_method(D_METHOD("get_igd_our_addr"), &UPNPDevice::get_igd_our_addr);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_our_addr"), "set_igd_our_addr", "get_igd_our_addr");
+
+ ClassDB::bind_method(D_METHOD("set_igd_status", "status"), &UPNPDevice::set_igd_status);
+ ClassDB::bind_method(D_METHOD("get_igd_status"), &UPNPDevice::get_igd_status);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "igd_status", PROPERTY_HINT_ENUM), "set_igd_status", "get_igd_status");
+
+ BIND_ENUM_CONSTANT(IGD_STATUS_OK);
+ BIND_ENUM_CONSTANT(IGD_STATUS_HTTP_ERROR);
+ BIND_ENUM_CONSTANT(IGD_STATUS_HTTP_EMPTY);
+ BIND_ENUM_CONSTANT(IGD_STATUS_NO_URLS);
+ BIND_ENUM_CONSTANT(IGD_STATUS_NO_IGD);
+ BIND_ENUM_CONSTANT(IGD_STATUS_DISCONNECTED);
+ BIND_ENUM_CONSTANT(IGD_STATUS_UNKNOWN_DEVICE);
+ BIND_ENUM_CONSTANT(IGD_STATUS_INVALID_CONTROL);
+ BIND_ENUM_CONSTANT(IGD_STATUS_MALLOC_ERROR);
+ BIND_ENUM_CONSTANT(IGD_STATUS_UNKNOWN_ERROR);
+}
+
+UPNPDevice::UPNPDevice() {
+ description_url = "";
+ service_type = "";
+ igd_control_url = "";
+ igd_service_type = "";
+ igd_our_addr = "";
+ igd_status = IGD_STATUS_UNKNOWN_ERROR;
+}
+
+UPNPDevice::~UPNPDevice() {
+}
diff --git a/modules/upnp/upnpdevice.h b/modules/upnp/upnpdevice.h
new file mode 100644
index 00000000000..25c9fe1ba3e
--- /dev/null
+++ b/modules/upnp/upnpdevice.h
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* upnpdevice.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_UPNPDEVICE_H
+#define GODOT_UPNPDEVICE_H
+
+#include
+
+class UPNPDevice : public Reference {
+
+ GDCLASS(UPNPDevice, Reference);
+
+public:
+ enum IGDStatus {
+
+ IGD_STATUS_OK,
+ IGD_STATUS_HTTP_ERROR,
+ IGD_STATUS_HTTP_EMPTY,
+ IGD_STATUS_NO_URLS,
+ IGD_STATUS_NO_IGD,
+ IGD_STATUS_DISCONNECTED,
+ IGD_STATUS_UNKNOWN_DEVICE,
+ IGD_STATUS_INVALID_CONTROL,
+ IGD_STATUS_MALLOC_ERROR,
+ IGD_STATUS_UNKNOWN_ERROR,
+ };
+
+ void set_description_url(const String &url);
+ String get_description_url() const;
+
+ void set_service_type(const String &type);
+ String get_service_type() const;
+
+ void set_igd_control_url(const String &url);
+ String get_igd_control_url() const;
+
+ void set_igd_service_type(const String &type);
+ String get_igd_service_type() const;
+
+ void set_igd_our_addr(const String &addr);
+ String get_igd_our_addr() const;
+
+ void set_igd_status(IGDStatus status);
+ IGDStatus get_igd_status() const;
+
+ bool is_valid_gateway() const;
+ String query_external_address() const;
+ int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const;
+ int delete_port_mapping(int port, String proto = "UDP") const;
+
+ UPNPDevice();
+ ~UPNPDevice();
+
+protected:
+ static void _bind_methods();
+
+private:
+ String description_url;
+ String service_type;
+ String igd_control_url;
+ String igd_service_type;
+ String igd_our_addr;
+ IGDStatus igd_status;
+};
+
+VARIANT_ENUM_CAST(UPNPDevice::IGDStatus)
+
+#endif // GODOT_UPNPDEVICE_H
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 25d6e3df9ad..ac5fe21f1d7 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -264,6 +264,14 @@ File extracted from upstream release tarball `mbedtls-2.8.0-apache.tgz`:
Be sure to check the Godot addition to only redfine it when undefined or `< 0x0501` (PRed upstream).
- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update.
+## miniupnpc
+
+- Upstream: https://github.com/miniupnp/miniupnp/tree/master/miniupnpc
+- Version: 2.1 (git 25615e0, 2018-05-08) with modifications
+- License: BSD-3-Clause
+
+The only modified file is miniupnpcstrings.h, which was created for Godot (is usually autogenerated by cmake).
+
## minizip
- Upstream: http://www.zlib.net
diff --git a/thirdparty/miniupnpc/LICENSE b/thirdparty/miniupnpc/LICENSE
new file mode 100644
index 00000000000..08167337041
--- /dev/null
+++ b/thirdparty/miniupnpc/LICENSE
@@ -0,0 +1,27 @@
+MiniUPnPc
+Copyright (c) 2005-2016, Thomas BERNARD
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/thirdparty/miniupnpc/codelength.h b/thirdparty/miniupnpc/codelength.h
new file mode 100644
index 00000000000..ea0b005ffe8
--- /dev/null
+++ b/thirdparty/miniupnpc/codelength.h
@@ -0,0 +1,54 @@
+/* $Id: codelength.h,v 1.3 2011/07/30 13:10:05 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef CODELENGTH_H_INCLUDED
+#define CODELENGTH_H_INCLUDED
+
+/* Encode length by using 7bit per Byte :
+ * Most significant bit of each byte specifies that the
+ * following byte is part of the code */
+
+/* n : unsigned
+ * p : unsigned char *
+ */
+#define DECODELENGTH(n, p) n = 0; \
+ do { n = (n << 7) | (*p & 0x7f); } \
+ while((*(p++)&0x80) && (n<(1<<25)));
+
+/* n : unsigned
+ * READ : function/macro to read one byte (unsigned char)
+ */
+#define DECODELENGTH_READ(n, READ) \
+ n = 0; \
+ do { \
+ unsigned char c; \
+ READ(c); \
+ n = (n << 7) | (c & 0x07f); \
+ if(!(c&0x80)) break; \
+ } while(n<(1<<25));
+
+/* n : unsigned
+ * p : unsigned char *
+ * p_limit : unsigned char *
+ */
+#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
+ n = 0; \
+ do { \
+ if((p) >= (p_limit)) break; \
+ n = (n << 7) | (*(p) & 0x7f); \
+ } while((*((p)++)&0x80) && (n<(1<<25)));
+
+
+/* n : unsigned
+ * p : unsigned char *
+ */
+#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
+ if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
+ if(n>=16384) *(p++) = (n >> 14) | 0x80; \
+ if(n>=128) *(p++) = (n >> 7) | 0x80; \
+ *(p++) = n & 0x7f;
+
+#endif /* CODELENGTH_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/connecthostport.c b/thirdparty/miniupnpc/connecthostport.c
new file mode 100644
index 00000000000..ea6e4e59437
--- /dev/null
+++ b/thirdparty/miniupnpc/connecthostport.c
@@ -0,0 +1,264 @@
+/* $Id: connecthostport.c,v 1.15 2015/10/09 16:26:19 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2010-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+/* use getaddrinfo() or gethostbyname()
+ * uncomment the following line in order to use gethostbyname() */
+#ifdef NO_GETADDRINFO
+#define USE_GETHOSTBYNAME
+#endif
+
+#include
+#include
+#ifdef _WIN32
+#include
+#include
+#include
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define herror
+#define socklen_t int
+#else /* #ifdef _WIN32 */
+#include
+#include
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#include
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+#include
+#include
+#include
+#define closesocket close
+#include
+#include
+/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
+ * during the connect() call */
+#define MINIUPNPC_IGNORE_EINTR
+#include
+#include
+#endif /* #else _WIN32 */
+
+/* definition of PRINT_SOCKET_ERROR */
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+#define herror(A) printf("%s\n", A)
+#endif
+
+#include "connecthostport.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+SOCKET connecthostport(const char * host, unsigned short port,
+ unsigned int scope_id)
+{
+ SOCKET s;
+ int n;
+#ifdef USE_GETHOSTBYNAME
+ struct sockaddr_in dest;
+ struct hostent *hp;
+#else /* #ifdef USE_GETHOSTBYNAME */
+ char tmp_host[MAXHOSTNAMELEN+1];
+ char port_str[8];
+ struct addrinfo *ai, *p;
+ struct addrinfo hints;
+#endif /* #ifdef USE_GETHOSTBYNAME */
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+
+#ifdef USE_GETHOSTBYNAME
+ hp = gethostbyname(host);
+ if(hp == NULL)
+ {
+ herror(host);
+ return INVALID_SOCKET;
+ }
+ memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
+ memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if(ISINVALID(s))
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return INVALID_SOCKET;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ dest.sin_family = AF_INET;
+ dest.sin_port = htons(port);
+ n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
+#ifdef MINIUPNPC_IGNORE_EINTR
+ /* EINTR The system call was interrupted by a signal that was caught
+ * EINPROGRESS The socket is nonblocking and the connection cannot
+ * be completed immediately. */
+ while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ return INVALID_SOCKET;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n<0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ closesocket(s);
+ return INVALID_SOCKET;
+ }
+#else /* #ifdef USE_GETHOSTBYNAME */
+ /* use getaddrinfo() instead of gethostbyname() */
+ memset(&hints, 0, sizeof(hints));
+ /* hints.ai_flags = AI_ADDRCONFIG; */
+#ifdef AI_NUMERICSERV
+ hints.ai_flags = AI_NUMERICSERV;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
+ /* hints.ai_protocol = IPPROTO_TCP; */
+ snprintf(port_str, sizeof(port_str), "%hu", port);
+ if(host[0] == '[')
+ {
+ /* literal ip v6 address */
+ int i, j;
+ for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
+ {
+ tmp_host[i] = host[j];
+ if(0 == memcmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */
+ j+=2; /* skip "25" */
+ }
+ tmp_host[i] = '\0';
+ }
+ else
+ {
+ strncpy(tmp_host, host, MAXHOSTNAMELEN);
+ }
+ tmp_host[MAXHOSTNAMELEN] = '\0';
+ n = getaddrinfo(tmp_host, port_str, &hints, &ai);
+ if(n != 0)
+ {
+#ifdef _WIN32
+ fprintf(stderr, "getaddrinfo() error : %d\n", n);
+#else
+ fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
+#endif
+ return INVALID_SOCKET;
+ }
+ s = -1;
+ for(p = ai; p; p = p->ai_next)
+ {
+ s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if(ISINVALID(s))
+ continue;
+ if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
+ struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
+ addr6->sin6_scope_id = scope_id;
+ }
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+ /* setting a 3 seconds timeout for the connect() call */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ n = connect(s, p->ai_addr, p->ai_addrlen);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ /* EINTR The system call was interrupted by a signal that was caught
+ * EINPROGRESS The socket is nonblocking and the connection cannot
+ * be completed immediately. */
+ while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
+ {
+ socklen_t len;
+ fd_set wset;
+ int err;
+ FD_ZERO(&wset);
+ FD_SET(s, &wset);
+ if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+ continue;
+ /*len = 0;*/
+ /*n = getpeername(s, NULL, &len);*/
+ len = sizeof(err);
+ if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ PRINT_SOCKET_ERROR("getsockopt");
+ closesocket(s);
+ freeaddrinfo(ai);
+ return INVALID_SOCKET;
+ }
+ if(err != 0) {
+ errno = err;
+ n = -1;
+ }
+ }
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+ if(n < 0)
+ {
+ closesocket(s);
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ freeaddrinfo(ai);
+ if(ISINVALID(s))
+ {
+ PRINT_SOCKET_ERROR("socket");
+ return INVALID_SOCKET;
+ }
+ if(n < 0)
+ {
+ PRINT_SOCKET_ERROR("connect");
+ return INVALID_SOCKET;
+ }
+#endif /* #ifdef USE_GETHOSTBYNAME */
+ return s;
+}
+
diff --git a/thirdparty/miniupnpc/connecthostport.h b/thirdparty/miniupnpc/connecthostport.h
new file mode 100644
index 00000000000..701816b5b67
--- /dev/null
+++ b/thirdparty/miniupnpc/connecthostport.h
@@ -0,0 +1,20 @@
+/* $Id: connecthostport.h,v 1.2 2012/06/23 22:32:33 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2010-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef CONNECTHOSTPORT_H_INCLUDED
+#define CONNECTHOSTPORT_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or INVALID_SOCKET in case of error */
+SOCKET connecthostport(const char * host, unsigned short port,
+ unsigned int scope_id);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/igd_desc_parse.c b/thirdparty/miniupnpc/igd_desc_parse.c
new file mode 100644
index 00000000000..d2999ad011c
--- /dev/null
+++ b/thirdparty/miniupnpc/igd_desc_parse.c
@@ -0,0 +1,123 @@
+/* $Id: igd_desc_parse.c,v 1.17 2015/09/15 13:30:04 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include "igd_desc_parse.h"
+#include
+#include
+
+/* Start element handler :
+ * update nesting level counter and copy element name */
+void IGDstartelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ if(l >= MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE-1;
+ memcpy(datas->cureltname, name, l);
+ datas->cureltname[l] = '\0';
+ datas->level++;
+ if( (l==7) && !memcmp(name, "service", l) ) {
+ datas->tmp.controlurl[0] = '\0';
+ datas->tmp.eventsuburl[0] = '\0';
+ datas->tmp.scpdurl[0] = '\0';
+ datas->tmp.servicetype[0] = '\0';
+ }
+}
+
+#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
+
+/* End element handler :
+ * update nesting level counter and update parser state if
+ * service element is parsed */
+void IGDendelt(void * d, const char * name, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ datas->level--;
+ /*printf("endelt %2d %.*s\n", datas->level, l, name);*/
+ if( (l==7) && !memcmp(name, "service", l) )
+ {
+ if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
+ memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
+ memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else if(COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANIPConnection:")
+ || COMPARE(datas->tmp.servicetype,
+ "urn:schemas-upnp-org:service:WANPPPConnection:") ) {
+ if(datas->first.servicetype[0] == '\0') {
+ memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
+ } else {
+ memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
+ }
+ }
+ }
+}
+
+/* Data handler :
+ * copy data depending on the current element name and state */
+void IGDdata(void * d, const char * data, int l)
+{
+ struct IGDdatas * datas = (struct IGDdatas *)d;
+ char * dstmember = 0;
+ /*printf("%2d %s : %.*s\n",
+ datas->level, datas->cureltname, l, data); */
+ if( !strcmp(datas->cureltname, "URLBase") )
+ dstmember = datas->urlbase;
+ else if( !strcmp(datas->cureltname, "presentationURL") )
+ dstmember = datas->presentationurl;
+ else if( !strcmp(datas->cureltname, "serviceType") )
+ dstmember = datas->tmp.servicetype;
+ else if( !strcmp(datas->cureltname, "controlURL") )
+ dstmember = datas->tmp.controlurl;
+ else if( !strcmp(datas->cureltname, "eventSubURL") )
+ dstmember = datas->tmp.eventsuburl;
+ else if( !strcmp(datas->cureltname, "SCPDURL") )
+ dstmember = datas->tmp.scpdurl;
+/* else if( !strcmp(datas->cureltname, "deviceType") )
+ dstmember = datas->devicetype_tmp;*/
+ if(dstmember)
+ {
+ if(l>=MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE-1;
+ memcpy(dstmember, data, l);
+ dstmember[l] = '\0';
+ }
+}
+
+#ifdef DEBUG
+void printIGD(struct IGDdatas * d)
+{
+ printf("urlbase = '%s'\n", d->urlbase);
+ printf("WAN Device (Common interface config) :\n");
+ /*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
+ printf(" serviceType = '%s'\n", d->CIF.servicetype);
+ printf(" controlURL = '%s'\n", d->CIF.controlurl);
+ printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
+ printf("primary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->first.devicetype);*/
+ printf(" servicetype = '%s'\n", d->first.servicetype);
+ printf(" controlURL = '%s'\n", d->first.controlurl);
+ printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->first.scpdurl);
+ printf("secondary WAN Connection Device (IP or PPP Connection):\n");
+ /*printf(" deviceType = '%s'\n", d->second.devicetype);*/
+ printf(" servicetype = '%s'\n", d->second.servicetype);
+ printf(" controlURL = '%s'\n", d->second.controlurl);
+ printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->second.scpdurl);
+ printf("WAN IPv6 Firewall Control :\n");
+ /*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
+ printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
+ printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
+ printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
+ printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
+}
+#endif /* DEBUG */
+
diff --git a/thirdparty/miniupnpc/igd_desc_parse.h b/thirdparty/miniupnpc/igd_desc_parse.h
new file mode 100644
index 00000000000..0de546b6979
--- /dev/null
+++ b/thirdparty/miniupnpc/igd_desc_parse.h
@@ -0,0 +1,49 @@
+/* $Id: igd_desc_parse.h,v 1.12 2014/11/17 17:19:13 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2014 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef IGD_DESC_PARSE_H_INCLUDED
+#define IGD_DESC_PARSE_H_INCLUDED
+
+/* Structure to store the result of the parsing of UPnP
+ * descriptions of Internet Gateway Devices */
+#define MINIUPNPC_URL_MAXSIZE (128)
+struct IGDdatas_service {
+ char controlurl[MINIUPNPC_URL_MAXSIZE];
+ char eventsuburl[MINIUPNPC_URL_MAXSIZE];
+ char scpdurl[MINIUPNPC_URL_MAXSIZE];
+ char servicetype[MINIUPNPC_URL_MAXSIZE];
+ /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
+};
+
+struct IGDdatas {
+ char cureltname[MINIUPNPC_URL_MAXSIZE];
+ char urlbase[MINIUPNPC_URL_MAXSIZE];
+ char presentationurl[MINIUPNPC_URL_MAXSIZE];
+ int level;
+ /*int state;*/
+ /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
+ struct IGDdatas_service CIF;
+ /* "urn:schemas-upnp-org:service:WANIPConnection:1"
+ * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
+ struct IGDdatas_service first;
+ /* if both WANIPConnection and WANPPPConnection are present */
+ struct IGDdatas_service second;
+ /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
+ struct IGDdatas_service IPv6FC;
+ /* tmp */
+ struct IGDdatas_service tmp;
+};
+
+void IGDstartelt(void *, const char *, int);
+void IGDendelt(void *, const char *, int);
+void IGDdata(void *, const char *, int);
+#ifdef DEBUG
+void printIGD(struct IGDdatas *);
+#endif /* DEBUG */
+
+#endif /* IGD_DESC_PARSE_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/listdevices.c b/thirdparty/miniupnpc/listdevices.c
new file mode 100644
index 00000000000..bd9ba57efcd
--- /dev/null
+++ b/thirdparty/miniupnpc/listdevices.c
@@ -0,0 +1,197 @@
+/* $Id: listdevices.c,v 1.6 2015/07/23 20:40:08 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2013-2015 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#endif /* _WIN32 */
+#include "miniupnpc.h"
+
+struct upnp_dev_list {
+ struct upnp_dev_list * next;
+ char * descURL;
+ struct UPNPDev * * array;
+ size_t count;
+ size_t allocated_count;
+};
+
+#define ADD_DEVICE_COUNT_STEP 16
+
+void add_device(struct upnp_dev_list * * list_head, struct UPNPDev * dev)
+{
+ struct upnp_dev_list * elt;
+ size_t i;
+
+ if(dev == NULL)
+ return;
+ for(elt = *list_head; elt != NULL; elt = elt->next) {
+ if(strcmp(elt->descURL, dev->descURL) == 0) {
+ for(i = 0; i < elt->count; i++) {
+ if (strcmp(elt->array[i]->st, dev->st) == 0 && strcmp(elt->array[i]->usn, dev->usn) == 0) {
+ return; /* already found */
+ }
+ }
+ if(elt->count >= elt->allocated_count) {
+ struct UPNPDev * * tmp;
+ elt->allocated_count += ADD_DEVICE_COUNT_STEP;
+ tmp = realloc(elt->array, elt->allocated_count * sizeof(struct UPNPDev *));
+ if(tmp == NULL) {
+ fprintf(stderr, "Failed to realloc(%p, %lu)\n", elt->array, (unsigned long)(elt->allocated_count * sizeof(struct UPNPDev *)));
+ return;
+ }
+ elt->array = tmp;
+ }
+ elt->array[elt->count++] = dev;
+ return;
+ }
+ }
+ elt = malloc(sizeof(struct upnp_dev_list));
+ if(elt == NULL) {
+ fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)sizeof(struct upnp_dev_list));
+ return;
+ }
+ elt->next = *list_head;
+ elt->descURL = strdup(dev->descURL);
+ if(elt->descURL == NULL) {
+ fprintf(stderr, "Failed to strdup(%s)\n", dev->descURL);
+ free(elt);
+ return;
+ }
+ elt->allocated_count = ADD_DEVICE_COUNT_STEP;
+ elt->array = malloc(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *));
+ if(elt->array == NULL) {
+ fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *)));
+ free(elt->descURL);
+ free(elt);
+ return;
+ }
+ elt->array[0] = dev;
+ elt->count = 1;
+ *list_head = elt;
+}
+
+void free_device(struct upnp_dev_list * elt)
+{
+ free(elt->descURL);
+ free(elt->array);
+ free(elt);
+}
+
+int main(int argc, char * * argv)
+{
+ const char * searched_device = NULL;
+ const char * * searched_devices = NULL;
+ const char * multicastif = 0;
+ const char * minissdpdpath = 0;
+ int ipv6 = 0;
+ unsigned char ttl = 2;
+ int error = 0;
+ struct UPNPDev * devlist = 0;
+ struct UPNPDev * dev;
+ struct upnp_dev_list * sorted_list = NULL;
+ struct upnp_dev_list * dev_array;
+ int i;
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+
+ for(i = 1; i < argc; i++) {
+ if(strcmp(argv[i], "-6") == 0)
+ ipv6 = 1;
+ else if(strcmp(argv[i], "-d") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "%s option needs one argument\n", "-d");
+ return 1;
+ }
+ searched_device = argv[i];
+ } else if(strcmp(argv[i], "-t") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "%s option needs one argument\n", "-t");
+ return 1;
+ }
+ ttl = (unsigned char)atoi(argv[i]);
+ } else if(strcmp(argv[i], "-l") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "-l option needs at least one argument\n");
+ return 1;
+ }
+ searched_devices = (const char * *)(argv + i);
+ break;
+ } else if(strcmp(argv[i], "-m") == 0) {
+ if(++i >= argc) {
+ fprintf(stderr, "-m option needs one argument\n");
+ return 1;
+ }
+ multicastif = argv[i];
+ } else {
+ printf("usage : %s [options] [-l ...]\n", argv[0]);
+ printf("options :\n");
+ printf(" -6 : use IPv6\n");
+ printf(" -m address/ifname : network interface to use for multicast\n");
+ printf(" -d : search only for this type of device\n");
+ printf(" -l ... : search only for theses types of device\n");
+ printf(" -t ttl : set multicast TTL. Default value is 2.\n");
+ printf(" -h : this help\n");
+ return 1;
+ }
+ }
+
+ if(searched_device) {
+ printf("searching UPnP device type %s\n", searched_device);
+ devlist = upnpDiscoverDevice(searched_device,
+ 2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error);
+ } else if(searched_devices) {
+ printf("searching UPnP device types :\n");
+ for(i = 0; searched_devices[i]; i++)
+ printf("\t%s\n", searched_devices[i]);
+ devlist = upnpDiscoverDevices(searched_devices,
+ 2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error, 1);
+ } else {
+ printf("searching all UPnP devices\n");
+ devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath,
+ 0/*localport*/, ipv6, ttl, &error);
+ }
+ if(devlist) {
+ for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) {
+ printf("%3d: %-48s\n", i, dev->st);
+ printf(" %s\n", dev->descURL);
+ printf(" %s\n", dev->usn);
+ add_device(&sorted_list, dev);
+ }
+ putchar('\n');
+ for (dev_array = sorted_list; dev_array != NULL ; dev_array = dev_array->next) {
+ printf("%s :\n", dev_array->descURL);
+ for(i = 0; (unsigned)i < dev_array->count; i++) {
+ printf("%2d: %s\n", i+1, dev_array->array[i]->st);
+ printf(" %s\n", dev_array->array[i]->usn);
+ }
+ putchar('\n');
+ }
+ freeUPNPDevlist(devlist);
+ while(sorted_list != NULL) {
+ dev_array = sorted_list;
+ sorted_list = sorted_list->next;
+ free_device(dev_array);
+ }
+ } else {
+ printf("no device found.\n");
+ }
+
+ return 0;
+}
+
diff --git a/thirdparty/miniupnpc/minisoap.c b/thirdparty/miniupnpc/minisoap.c
new file mode 100644
index 00000000000..520c9302e8d
--- /dev/null
+++ b/thirdparty/miniupnpc/minisoap.c
@@ -0,0 +1,124 @@
+/* $Id: minisoap.c,v 1.25 2017/04/21 10:03:24 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ *
+ * Minimal SOAP implementation for UPnP protocol.
+ */
+#include
+#include
+#ifdef _WIN32
+#include
+#include
+#define snprintf _snprintf
+#else
+#include
+#include
+#include
+#endif
+#include "minisoap.h"
+#include "miniupnpcstrings.h"
+
+/* only for malloc */
+#include
+
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+/* httpWrite sends the headers and the body to the socket
+ * and returns the number of bytes sent */
+static int
+httpWrite(SOCKET fd, const char * body, int bodysize,
+ const char * headers, int headerssize)
+{
+ int n = 0;
+ /*n = write(fd, headers, headerssize);*/
+ /*if(bodysize>0)
+ n += write(fd, body, bodysize);*/
+ /* Note : my old linksys router only took into account
+ * soap request that are sent into only one packet */
+ char * p;
+ /* TODO: AVOID MALLOC, we could use writev() for that */
+ p = malloc(headerssize+bodysize);
+ if(!p)
+ return -1;
+ memcpy(p, headers, headerssize);
+ memcpy(p+headerssize, body, bodysize);
+ /*n = write(fd, p, headerssize+bodysize);*/
+ n = send(fd, p, headerssize+bodysize, 0);
+ if(n<0) {
+ PRINT_SOCKET_ERROR("send");
+ }
+ /* disable send on the socket */
+ /* draytek routers don't seem to like that... */
+#if 0
+#ifdef _WIN32
+ if(shutdown(fd, SD_SEND)<0) {
+#else
+ if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
+#endif
+ PRINT_SOCKET_ERROR("shutdown");
+ }
+#endif
+ free(p);
+ return n;
+}
+
+/* self explanatory */
+int soapPostSubmit(SOCKET fd,
+ const char * url,
+ const char * host,
+ unsigned short port,
+ const char * action,
+ const char * body,
+ const char * httpversion)
+{
+ int bodysize;
+ char headerbuf[512];
+ int headerssize;
+ char portstr[8];
+ bodysize = (int)strlen(body);
+ /* We are not using keep-alive HTTP connections.
+ * HTTP/1.1 needs the header Connection: close to do that.
+ * This is the default with HTTP/1.0
+ * Using HTTP/1.1 means we need to support chunked transfer-encoding :
+ * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
+ * transfer encoding. */
+ /* Connection: Close is normally there only in HTTP/1.1 but who knows */
+ portstr[0] = '\0';
+ if(port != 80)
+ snprintf(portstr, sizeof(portstr), ":%hu", port);
+ headerssize = snprintf(headerbuf, sizeof(headerbuf),
+ "POST %s HTTP/%s\r\n"
+ "Host: %s%s\r\n"
+ "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+ "Content-Length: %d\r\n"
+ "Content-Type: text/xml\r\n"
+ "SOAPAction: \"%s\"\r\n"
+ "Connection: Close\r\n"
+ "Cache-Control: no-cache\r\n" /* ??? */
+ "Pragma: no-cache\r\n"
+ "\r\n",
+ url, httpversion, host, portstr, bodysize, action);
+ if ((unsigned int)headerssize >= sizeof(headerbuf))
+ return -1;
+#ifdef DEBUG
+ /*printf("SOAP request : headersize=%d bodysize=%d\n",
+ headerssize, bodysize);
+ */
+ printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
+ url, httpversion, host, portstr);
+ printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
+ printf("Headers :\n%s", headerbuf);
+ printf("Body :\n%s\n", body);
+#endif
+ return httpWrite(fd, body, bodysize, headerbuf, headerssize);
+}
+
+
diff --git a/thirdparty/miniupnpc/minisoap.h b/thirdparty/miniupnpc/minisoap.h
new file mode 100644
index 00000000000..d6a45d03ba2
--- /dev/null
+++ b/thirdparty/miniupnpc/minisoap.h
@@ -0,0 +1,17 @@
+/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+#ifndef MINISOAP_H_INCLUDED
+#define MINISOAP_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+/*int httpWrite(int, const char *, int, const char *);*/
+int soapPostSubmit(SOCKET, const char *, const char *, unsigned short,
+ const char *, const char *, const char *);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/minissdpc.c b/thirdparty/miniupnpc/minissdpc.c
new file mode 100644
index 00000000000..d76b242ad0d
--- /dev/null
+++ b/thirdparty/miniupnpc/minissdpc.c
@@ -0,0 +1,888 @@
+/* $Id: minissdpc.c,v 1.32 2016/10/07 09:04:36 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+/*#include */
+#include
+#include
+#include
+#include
+#if defined (__NetBSD__)
+#include
+#endif
+#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
+#ifdef _WIN32
+#include
+#include
+#include
+#include
+#define snprintf _snprintf
+#if !defined(_MSC_VER)
+#include
+#else /* !defined(_MSC_VER) */
+typedef unsigned short uint16_t;
+#endif /* !defined(_MSC_VER) */
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#endif /* _WIN32 */
+#if defined(__amigaos__) || defined(__amigaos4__)
+#include
+#endif /* defined(__amigaos__) || defined(__amigaos4__) */
+#if defined(__amigaos__)
+#define uint16_t unsigned short
+#endif /* defined(__amigaos__) */
+/* Hack */
+#define UNIX_PATH_LEN 108
+struct sockaddr_un {
+ uint16_t sun_family;
+ char sun_path[UNIX_PATH_LEN];
+};
+#else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#define closesocket close
+#endif
+
+#include "miniupnpc_socketdef.h"
+
+#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__)
+#define HAS_IP_MREQN
+#endif
+
+#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
+#include
+#if defined(__sun)
+#include
+#endif
+#endif
+
+#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
+/* Several versions of glibc don't define this structure,
+ * define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
+struct ip_mreqn
+{
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_address; /* local IP address of interface */
+ int imr_ifindex; /* Interface index */
+};
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+/* Amiga OS specific stuff */
+#define TIMEVAL struct timeval
+#endif
+
+#include "minissdpc.h"
+#include "miniupnpc.h"
+#include "receivedata.h"
+
+#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
+
+#include "codelength.h"
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error)
+{
+ struct UPNPDev * devlist = NULL;
+ int s;
+ int res;
+
+ s = connectToMiniSSDPD(socketpath);
+ if (s < 0) {
+ if (error)
+ *error = s;
+ return NULL;
+ }
+ res = requestDevicesFromMiniSSDPD(s, devtype);
+ if (res < 0) {
+ if (error)
+ *error = res;
+ } else {
+ devlist = receiveDevicesFromMiniSSDPD(s, error);
+ }
+ disconnectFromMiniSSDPD(s);
+ return devlist;
+}
+
+/* macros used to read from unix socket */
+#define READ_BYTE_BUFFER(c) \
+ if((int)bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ c = buffer[bufferindex++];
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+#define READ_COPY_BUFFER(dst, len) \
+ for(l = len, p = (unsigned char *)dst; l > 0; ) { \
+ unsigned int lcopy; \
+ if((int)bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ lcopy = MIN(l, (n - bufferindex)); \
+ memcpy(p, buffer + bufferindex, lcopy); \
+ l -= lcopy; \
+ p += lcopy; \
+ bufferindex += lcopy; \
+ }
+
+#define READ_DISCARD_BUFFER(len) \
+ for(l = len; l > 0; ) { \
+ unsigned int lcopy; \
+ if(bufferindex >= n) { \
+ n = read(s, buffer, sizeof(buffer)); \
+ if(n<=0) break; \
+ bufferindex = 0; \
+ } \
+ lcopy = MIN(l, (n - bufferindex)); \
+ l -= lcopy; \
+ bufferindex += lcopy; \
+ }
+
+int
+connectToMiniSSDPD(const char * socketpath)
+{
+ int s;
+ struct sockaddr_un addr;
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
+ struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(s < 0)
+ {
+ /*syslog(LOG_ERR, "socket(unix): %m");*/
+ perror("socket(unix)");
+ return MINISSDPC_SOCKET_ERROR;
+ }
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
+ /* setting a 3 seconds timeout */
+ /* not supported for AF_UNIX sockets under Solaris */
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ perror("setsockopt SO_RCVTIMEO unix");
+ }
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+ {
+ perror("setsockopt SO_SNDTIMEO unix");
+ }
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+ if(!socketpath)
+ socketpath = "/var/run/minissdpd.sock";
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
+ /* TODO : check if we need to handle the EINTR */
+ if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
+ {
+ /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
+ close(s);
+ return MINISSDPC_SOCKET_ERROR;
+ }
+ return s;
+}
+
+int
+disconnectFromMiniSSDPD(int s)
+{
+ if (close(s) < 0)
+ return MINISSDPC_SOCKET_ERROR;
+ return MINISSDPC_SUCCESS;
+}
+
+int
+requestDevicesFromMiniSSDPD(int s, const char * devtype)
+{
+ unsigned char buffer[256];
+ unsigned char * p;
+ unsigned int stsize, l;
+
+ stsize = strlen(devtype);
+ if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8))
+ {
+ buffer[0] = 3; /* request type 3 : everything */
+ }
+ else
+ {
+ buffer[0] = 1; /* request type 1 : request devices/services by type */
+ }
+ p = buffer + 1;
+ l = stsize; CODELENGTH(l, p);
+ if(p + stsize > buffer + sizeof(buffer))
+ {
+ /* devtype is too long ! */
+#ifdef DEBUG
+ fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n",
+ stsize, (unsigned)sizeof(buffer));
+#endif /* DEBUG */
+ return MINISSDPC_INVALID_INPUT;
+ }
+ memcpy(p, devtype, stsize);
+ p += stsize;
+ if(write(s, buffer, p - buffer) < 0)
+ {
+ /*syslog(LOG_ERR, "write(): %m");*/
+ perror("minissdpc.c: write()");
+ return MINISSDPC_SOCKET_ERROR;
+ }
+ return MINISSDPC_SUCCESS;
+}
+
+struct UPNPDev *
+receiveDevicesFromMiniSSDPD(int s, int * error)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = NULL;
+ unsigned char buffer[256];
+ ssize_t n;
+ unsigned char * p;
+ unsigned char * url;
+ unsigned char * st;
+ unsigned int bufferindex;
+ unsigned int i, ndev;
+ unsigned int urlsize, stsize, usnsize, l;
+
+ n = read(s, buffer, sizeof(buffer));
+ if(n<=0)
+ {
+ perror("minissdpc.c: read()");
+ if (error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ return NULL;
+ }
+ ndev = buffer[0];
+ bufferindex = 1;
+ for(i = 0; i < ndev; i++)
+ {
+ DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ return devlist;
+ }
+#ifdef DEBUG
+ printf(" urlsize=%u", urlsize);
+#endif /* DEBUG */
+ url = malloc(urlsize);
+ if(url == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ return devlist;
+ }
+ READ_COPY_BUFFER(url, urlsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_return;
+ }
+ DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_return;
+ }
+#ifdef DEBUG
+ printf(" stsize=%u", stsize);
+#endif /* DEBUG */
+ st = malloc(stsize);
+ if (st == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto free_url_and_return;
+ }
+ READ_COPY_BUFFER(st, stsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_st_and_return;
+ }
+ DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_url_and_st_and_return;
+ }
+#ifdef DEBUG
+ printf(" usnsize=%u\n", usnsize);
+#endif /* DEBUG */
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
+ if(tmp == NULL) {
+ if (error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto free_url_and_st_and_return;
+ }
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ memcpy(tmp->buffer, url, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->st, st, stsize);
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ free(url);
+ free(st);
+ url = NULL;
+ st = NULL;
+ tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize;
+ READ_COPY_BUFFER(tmp->usn, usnsize);
+ if(n<=0) {
+ if (error)
+ *error = MINISSDPC_INVALID_SERVER_REPLY;
+ goto free_tmp_and_return;
+ }
+ tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
+ tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */
+ devlist = tmp;
+ }
+ if (error)
+ *error = MINISSDPC_SUCCESS;
+ return devlist;
+
+free_url_and_st_and_return:
+ free(st);
+free_url_and_return:
+ free(url);
+ return devlist;
+
+free_tmp_and_return:
+ free(tmp);
+ return devlist;
+}
+
+#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
+
+/* parseMSEARCHReply()
+ * the last 4 arguments are filled during the parsing :
+ * - location/locationsize : "location:" field of the SSDP reply packet
+ * - st/stsize : "st:" field of the SSDP reply packet.
+ * The strings are NOT null terminated */
+static void
+parseMSEARCHReply(const char * reply, int size,
+ const char * * location, int * locationsize,
+ const char * * st, int * stsize,
+ const char * * usn, int * usnsize)
+{
+ int a, b, i;
+ i = 0;
+ a = i; /* start of the line */
+ b = 0; /* end of the "header" (position of the colon) */
+ while(isin6_family = AF_INET6;
+ if(localport > 0 && localport < 65536)
+ p->sin6_port = htons((unsigned short)localport);
+ p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
+ p->sin_family = AF_INET;
+ if(localport > 0 && localport < 65536)
+ p->sin_port = htons((unsigned short)localport);
+ p->sin_addr.s_addr = INADDR_ANY;
+ }
+#ifdef _WIN32
+/* This code could help us to use the right Network interface for
+ * SSDP multicast traffic */
+/* Get IP associated with the index given in the ip_forward struct
+ * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
+ if(!ipv6
+ && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
+ DWORD dwRetVal = 0;
+ PMIB_IPADDRTABLE pIPAddrTable;
+ DWORD dwSize = 0;
+#ifdef DEBUG
+ IN_ADDR IPAddr;
+#endif
+ int i;
+#ifdef DEBUG
+ printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
+#endif
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
+ if(pIPAddrTable) {
+ if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
+ free(pIPAddrTable);
+ pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
+ }
+ }
+ if(pIPAddrTable) {
+ dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
+ if (dwRetVal == NO_ERROR) {
+#ifdef DEBUG
+ printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
+#endif
+ for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
+#ifdef DEBUG
+ printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
+ printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
+ printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
+ printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
+ printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
+ printf("\tType and State[%d]:", i);
+ printf("\n");
+#endif
+ if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
+ /* Set the address of this interface to be used */
+ struct in_addr mc_if;
+ memset(&mc_if, 0, sizeof(mc_if));
+ mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
+ PRINT_SOCKET_ERROR("setsockopt");
+ }
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
+#ifndef DEBUG
+ break;
+#endif
+ }
+ }
+ }
+ free(pIPAddrTable);
+ pIPAddrTable = NULL;
+ }
+ }
+#endif /* _WIN32 */
+
+#ifdef _WIN32
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
+#else
+ if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
+#endif
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");
+ return NULL;
+ }
+
+ if(ipv6) {
+#ifdef _WIN32
+ DWORD mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)
+#else /* _WIN32 */
+ int mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
+#endif /* _WIN32 */
+ {
+ PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
+ }
+ } else {
+#ifdef _WIN32
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+#else /* _WIN32 */
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+#endif /* _WIN32 */
+ {
+ /* not a fatal error */
+ PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ }
+ }
+
+ if(multicastif)
+ {
+ if(ipv6) {
+#if !defined(_WIN32)
+ /* according to MSDN, if_nametoindex() is supported since
+ * MS Windows Vista and MS Windows Server 2008.
+ * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
+ unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
+ }
+#else
+#ifdef DEBUG
+ printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
+#endif
+#endif
+ } else {
+ struct in_addr mc_if;
+ mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
+ if(mc_if.s_addr != INADDR_NONE)
+ {
+ ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
+ }
+ } else {
+#ifdef HAS_IP_MREQN
+ /* was not an ip address, try with an interface name */
+ struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
+ memset(&reqn, 0, sizeof(struct ip_mreqn));
+ reqn.imr_ifindex = if_nametoindex(multicastif);
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
+ }
+#elif !defined(_WIN32)
+ struct ifreq ifr;
+ int ifrlen = sizeof(ifr);
+ strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ-1] = '\0';
+ if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
+ {
+ PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
+ }
+ mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
+ }
+#else /* _WIN32 */
+#ifdef DEBUG
+ printf("Setting of multicast interface not supported with interface name.\n");
+#endif
+#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */
+ }
+ }
+ }
+
+ /* Before sending the packed, we first "bind" in order to be able
+ * to receive the response */
+ if (bind(sudp, (const struct sockaddr *)&sockudp_r,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
+ {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("bind");
+ closesocket(sudp);
+ return NULL;
+ }
+
+ if(error)
+ *error = MINISSDPC_SUCCESS;
+ /* Calculating maximum response time in seconds */
+ mx = ((unsigned int)delay) / 1000u;
+ if(mx == 0) {
+ mx = 1;
+ delay = 1000;
+ }
+ /* receiving SSDP response packet */
+ for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ sentok = 0;
+ /* sending the SSDP M-SEARCH packet */
+ n = snprintf(bufr, sizeof(bufr),
+ MSearchMsgFmt,
+ ipv6 ?
+ (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
+ : UPNP_MCAST_ADDR,
+ deviceTypes[deviceIndex], mx);
+ if ((unsigned int)n >= sizeof(bufr)) {
+ if(error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto error;
+ }
+#ifdef DEBUG
+ /*printf("Sending %s", bufr);*/
+ printf("Sending M-SEARCH request to %s with ST: %s\n",
+ ipv6 ?
+ (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
+ : UPNP_MCAST_ADDR,
+ deviceTypes[deviceIndex]);
+#endif
+#ifdef NO_GETADDRINFO
+ /* the following code is not using getaddrinfo */
+ /* emission */
+ memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
+ if(ipv6) {
+ struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
+ p->sin6_family = AF_INET6;
+ p->sin6_port = htons(SSDP_PORT);
+ inet_pton(AF_INET6,
+ linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
+ &(p->sin6_addr));
+ } else {
+ struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
+ p->sin_family = AF_INET;
+ p->sin_port = htons(SSDP_PORT);
+ p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
+ }
+ n = sendto(sudp, bufr, n, 0, &sockudp_w,
+ ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+ if (n < 0) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ PRINT_SOCKET_ERROR("sendto");
+ } else {
+ sentok = 1;
+ }
+#else /* #ifdef NO_GETADDRINFO */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
+ hints.ai_socktype = SOCK_DGRAM;
+ /*hints.ai_flags = */
+ if ((rv = getaddrinfo(ipv6
+ ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
+ : UPNP_MCAST_ADDR,
+ XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+#ifdef _WIN32
+ fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
+#else
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+#endif
+ break;
+ }
+ for(p = servinfo; p; p = p->ai_next) {
+ n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
+ if (n < 0) {
+#ifdef DEBUG
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
+ sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
+ fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
+ }
+#endif
+ PRINT_SOCKET_ERROR("sendto");
+ continue;
+ } else {
+ sentok = 1;
+ }
+ }
+ freeaddrinfo(servinfo);
+ if(!sentok) {
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ }
+#endif /* #ifdef NO_GETADDRINFO */
+ /* Waiting for SSDP REPLY packet to M-SEARCH
+ * if searchalltypes is set, enter the loop only
+ * when the last deviceType is reached */
+ if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) do {
+ n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
+ if (n < 0) {
+ /* error */
+ if(error)
+ *error = MINISSDPC_SOCKET_ERROR;
+ goto error;
+ } else if (n == 0) {
+ /* no data or Time Out */
+#ifdef DEBUG
+ printf("NODATA or TIMEOUT\n");
+#endif /* DEBUG */
+ if (devlist && !searchalltypes) {
+ /* found some devices, stop now*/
+ if(error)
+ *error = MINISSDPC_SUCCESS;
+ goto error;
+ }
+ } else {
+ const char * descURL=NULL;
+ int urlsize=0;
+ const char * st=NULL;
+ int stsize=0;
+ const char * usn=NULL;
+ int usnsize=0;
+ parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
+ if(st&&descURL) {
+#ifdef DEBUG
+ printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",
+ stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
+#endif /* DEBUG */
+ for(tmp=devlist; tmp; tmp = tmp->pNext) {
+ if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
+ tmp->descURL[urlsize] == '\0' &&
+ memcmp(tmp->st, st, stsize) == 0 &&
+ tmp->st[stsize] == '\0' &&
+ (usnsize == 0 || memcmp(tmp->usn, usn, usnsize) == 0) &&
+ tmp->usn[usnsize] == '\0')
+ break;
+ }
+ /* at the exit of the loop above, tmp is null if
+ * no duplicate device was found */
+ if(tmp)
+ continue;
+ tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
+ if(!tmp) {
+ /* memory allocation error */
+ if(error)
+ *error = MINISSDPC_MEMORY_ERROR;
+ goto error;
+ }
+ tmp->pNext = devlist;
+ tmp->descURL = tmp->buffer;
+ tmp->st = tmp->buffer + 1 + urlsize;
+ tmp->usn = tmp->st + 1 + stsize;
+ memcpy(tmp->buffer, descURL, urlsize);
+ tmp->buffer[urlsize] = '\0';
+ memcpy(tmp->st, st, stsize);
+ tmp->buffer[urlsize+1+stsize] = '\0';
+ if(usn != NULL)
+ memcpy(tmp->usn, usn, usnsize);
+ tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
+ tmp->scope_id = scope_id;
+ devlist = tmp;
+ }
+ }
+ } while(n > 0);
+ if(ipv6) {
+ /* switch linklocal flag */
+ if(linklocal) {
+ linklocal = 0;
+ --deviceIndex;
+ } else {
+ linklocal = 1;
+ }
+ }
+ }
+error:
+ closesocket(sudp);
+ return devlist;
+}
+
diff --git a/thirdparty/miniupnpc/minissdpc.h b/thirdparty/miniupnpc/minissdpc.h
new file mode 100644
index 00000000000..167d897cb60
--- /dev/null
+++ b/thirdparty/miniupnpc/minissdpc.h
@@ -0,0 +1,58 @@
+/* $Id: minissdpc.h,v 1.6 2015/09/18 12:45:16 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINISSDPC_H_INCLUDED
+#define MINISSDPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define MINISSDPC_SUCCESS (0)
+#define MINISSDPC_UNKNOWN_ERROR (-1)
+#define MINISSDPC_SOCKET_ERROR (-101)
+#define MINISSDPC_MEMORY_ERROR (-102)
+#define MINISSDPC_INVALID_INPUT (-103)
+#define MINISSDPC_INVALID_SERVER_REPLY (-104)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error);
+
+MINIUPNP_LIBSPEC int
+connectToMiniSSDPD(const char * socketpath);
+
+MINIUPNP_LIBSPEC int
+disconnectFromMiniSSDPD(int fd);
+
+MINIUPNP_LIBSPEC int
+requestDevicesFromMiniSSDPD(int fd, const char * devtype);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+receiveDevicesFromMiniSSDPD(int fd, int * error);
+
+#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+ssdpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc.c b/thirdparty/miniupnpc/miniupnpc.c
new file mode 100644
index 00000000000..5d93ef99331
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc.c
@@ -0,0 +1,727 @@
+/* $Id: miniupnpc.c,v 1.149 2016/02/09 09:50:46 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#include
+#include
+#include
+#ifdef _WIN32
+/* Win32 Specific includes and defines */
+#include
+#include
+#include
+#include
+#define snprintf _snprintf
+#define strdup _strdup
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#define MAXHOSTNAMELEN 64
+#else /* #ifdef _WIN32 */
+/* Standard POSIX includes */
+#include
+#if defined(__amigaos__) && !defined(__amigaos4__)
+/* Amiga OS 3 specific stuff */
+#define socklen_t int
+#else
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include
+#endif
+#include
+#include
+#define closesocket close
+#endif /* #else _WIN32 */
+#ifdef __GNU__
+#define MAXHOSTNAMELEN 64
+#endif
+
+
+#include "miniupnpc.h"
+#include "minissdpc.h"
+#include "miniwget.h"
+#include "miniwget_private.h"
+#include "minisoap.h"
+#include "minixml.h"
+#include "upnpcommands.h"
+#include "connecthostport.h"
+
+/* compare the beginning of a string with a constant string */
+#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define SOAPPREFIX "s"
+#define SERVICEPREFIX "u"
+#define SERVICEPREFIX2 'u'
+
+/* check if an ip address is a private (LAN) address
+ * see https://tools.ietf.org/html/rfc1918 */
+static int is_rfc1918addr(const char * addr)
+{
+ /* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */
+ if(COMPARE(addr, "192.168."))
+ return 1;
+ /* 10.0.0.0 - 10.255.255.255 (10/8 prefix) */
+ if(COMPARE(addr, "10."))
+ return 1;
+ /* 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) */
+ if(COMPARE(addr, "172.")) {
+ int i = atoi(addr + 4);
+ if((16 <= i) && (i <= 31))
+ return 1;
+ }
+ return 0;
+}
+
+/* root description parsing */
+MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
+{
+ struct xmlparser parser;
+ /* xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = IGDstartelt;
+ parser.endeltfunc = IGDendelt;
+ parser.datafunc = IGDdata;
+ parser.attfunc = 0;
+ parsexml(&parser);
+#ifdef DEBUG
+ printIGD(data);
+#endif
+}
+
+/* simpleUPnPcommand2 :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+static char *
+simpleUPnPcommand2(SOCKET s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize, const char * httpversion)
+{
+ char hostname[MAXHOSTNAMELEN+1];
+ unsigned short port = 0;
+ char * path;
+ char soapact[128];
+ char soapbody[2048];
+ int soapbodylen;
+ char * buf;
+ int n;
+ int status_code;
+
+ *bufsize = 0;
+ snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
+ if(args==NULL)
+ {
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
+ "\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
+ "" SERVICEPREFIX ":%s>"
+ "" SOAPPREFIX ":Body>" SOAPPREFIX ":Envelope>"
+ "\r\n", action, service, action);
+ if ((unsigned int)soapbodylen >= sizeof(soapbody))
+ return NULL;
+ }
+ else
+ {
+ char * p;
+ const char * pe, * pv;
+ const char * const pend = soapbody + sizeof(soapbody);
+ soapbodylen = snprintf(soapbody, sizeof(soapbody),
+ "\r\n"
+ "<" SOAPPREFIX ":Envelope "
+ "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAPPREFIX ":Body>"
+ "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
+ action, service);
+ if ((unsigned int)soapbodylen >= sizeof(soapbody))
+ return NULL;
+ p = soapbody + soapbodylen;
+ while(args->elt)
+ {
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '<';
+
+ pe = args->elt;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '>';
+
+ if((pv = args->val))
+ {
+ while(p < pend && *pv)
+ *(p++) = *(pv++);
+ }
+
+ if((p+2) > pend) /* check for space to write next 2 bytes */
+ return NULL;
+ *(p++) = '<';
+ *(p++) = '/';
+
+ pe = args->elt;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ if(p >= pend) /* check for space to write next byte */
+ return NULL;
+ *(p++) = '>';
+
+ args++;
+ }
+ if((p+4) > pend) /* check for space to write next 4 bytes */
+ return NULL;
+ *(p++) = '<';
+ *(p++) = '/';
+ *(p++) = SERVICEPREFIX2;
+ *(p++) = ':';
+
+ pe = action;
+ while(p < pend && *pe)
+ *(p++) = *(pe++);
+
+ strncpy(p, ">" SOAPPREFIX ":Body>" SOAPPREFIX ":Envelope>\r\n",
+ pend - p);
+ if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */
+ return NULL;
+ }
+ if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
+ if(ISINVALID(s)) {
+ s = connecthostport(hostname, port, 0);
+ if(ISINVALID(s)) {
+ /* failed to connect */
+ return NULL;
+ }
+ }
+
+ n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
+ if(n<=0) {
+#ifdef DEBUG
+ printf("Error sending SOAP request\n");
+#endif
+ closesocket(s);
+ return NULL;
+ }
+
+ buf = getHTTPResponse(s, bufsize, &status_code);
+#ifdef DEBUG
+ if(*bufsize > 0 && buf)
+ {
+ printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf);
+ }
+ else
+ {
+ printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
+ }
+#endif
+ closesocket(s);
+ return buf;
+}
+
+/* simpleUPnPcommand :
+ * not so simple !
+ * return values :
+ * pointer - OK
+ * NULL - error */
+char *
+simpleUPnPcommand(int s, const char * url, const char * service,
+ const char * action, struct UPNParg * args,
+ int * bufsize)
+{
+ char * buf;
+
+#if 1
+ buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1");
+#else
+ buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.0");
+ if (!buf || *bufsize == 0)
+ {
+#if DEBUG
+ printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
+#endif
+ buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1");
+ }
+#endif
+ return buf;
+}
+
+/* upnpDiscoverDevices() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll).
+ * UDA v1.1 says :
+ * The TTL for the IP packet SHOULD default to 2 and
+ * SHOULD be configurable. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes)
+{
+ struct UPNPDev * tmp;
+ struct UPNPDev * devlist = 0;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ int deviceIndex;
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+
+ if(error)
+ *error = UPNPDISCOVER_UNKNOWN_ERROR;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* first try to get infos from minissdpd ! */
+ if(!minissdpdsock)
+ minissdpdsock = "/var/run/minissdpd.sock";
+ if(minissdpdsock[0] != '\0') {
+ for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ struct UPNPDev * minissdpd_devlist;
+ int only_rootdevice = 1;
+ minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
+ minissdpdsock, 0);
+ if(minissdpd_devlist) {
+#ifdef DEBUG
+ printf("returned by MiniSSDPD: %s\t%s\n",
+ minissdpd_devlist->st, minissdpd_devlist->descURL);
+#endif /* DEBUG */
+ if(!strstr(minissdpd_devlist->st, "rootdevice"))
+ only_rootdevice = 0;
+ for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) {
+#ifdef DEBUG
+ printf("returned by MiniSSDPD: %s\t%s\n",
+ tmp->pNext->st, tmp->pNext->descURL);
+#endif /* DEBUG */
+ if(!strstr(tmp->st, "rootdevice"))
+ only_rootdevice = 0;
+ }
+ tmp->pNext = devlist;
+ devlist = minissdpd_devlist;
+ if(!searchalltypes && !only_rootdevice)
+ break;
+ }
+ }
+ }
+ for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
+ /* We return what we have found if it was not only a rootdevice */
+ if(!strstr(tmp->st, "rootdevice")) {
+ if(error)
+ *error = UPNPDISCOVER_SUCCESS;
+ return devlist;
+ }
+ }
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+
+ /* direct discovery if minissdpd responses are not sufficient */
+ {
+ struct UPNPDev * discovered_devlist;
+ discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
+ ipv6, ttl, error, searchalltypes);
+ if(devlist == NULL)
+ devlist = discovered_devlist;
+ else {
+ for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext);
+ tmp->pNext = discovered_devlist;
+ }
+ }
+ return devlist;
+}
+
+/* upnpDiscover() Discover IGD device */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ static const char * const deviceList[] = {
+#if 0
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
+ "urn:schemas-upnp-org:service:WANIPConnection:2",
+#endif
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+ "urn:schemas-upnp-org:service:WANIPConnection:1",
+ "urn:schemas-upnp-org:service:WANPPPConnection:1",
+ "upnp:rootdevice",
+ /*"ssdp:all",*/
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+/* upnpDiscoverAll() Discover all UPnP devices */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ static const char * const deviceList[] = {
+ /*"upnp:rootdevice",*/
+ "ssdp:all",
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+/* upnpDiscoverDevice() Discover a specific device */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error)
+{
+ const char * const deviceList[] = {
+ device,
+ 0
+ };
+ return upnpDiscoverDevices(deviceList,
+ delay, multicastif, minissdpdsock, localport,
+ ipv6, ttl, error, 0);
+}
+
+static char *
+build_absolute_url(const char * baseurl, const char * descURL,
+ const char * url, unsigned int scope_id)
+{
+ int l, n;
+ char * s;
+ const char * base;
+ char * p;
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ char ifname[IF_NAMESIZE];
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ char scope_str[8];
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+
+ if( (url[0] == 'h')
+ &&(url[1] == 't')
+ &&(url[2] == 't')
+ &&(url[3] == 'p')
+ &&(url[4] == ':')
+ &&(url[5] == '/')
+ &&(url[6] == '/'))
+ return strdup(url);
+ base = (baseurl[0] == '\0') ? descURL : baseurl;
+ n = strlen(base);
+ if(n > 7) {
+ p = strchr(base + 7, '/');
+ if(p)
+ n = p - base;
+ }
+ l = n + strlen(url) + 1;
+ if(url[0] != '/')
+ l++;
+ if(scope_id != 0) {
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ if(if_indextoname(scope_id, ifname)) {
+ l += 3 + strlen(ifname); /* 3 == strlen(%25) */
+ }
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ /* under windows, scope is numerical */
+ l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ }
+ s = malloc(l);
+ if(s == NULL) return NULL;
+ memcpy(s, base, n);
+ if(scope_id != 0) {
+ s[n] = '\0';
+ if(0 == memcmp(s, "http://[fe80:", 13)) {
+ /* this is a linklocal IPv6 address */
+ p = strchr(s, ']');
+ if(p) {
+ /* insert %25 into URL */
+#if defined(IF_NAMESIZE) && !defined(_WIN32)
+ memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
+ memcpy(p, "%25", 3);
+ memcpy(p + 3, ifname, strlen(ifname));
+ n += 3 + strlen(ifname);
+#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
+ memcpy(p, "%25", 3);
+ memcpy(p + 3, scope_str, strlen(scope_str));
+ n += 3 + strlen(scope_str);
+#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
+ }
+ }
+ }
+ if(url[0] != '/')
+ s[n++] = '/';
+ memcpy(s + n, url, l - n);
+ return s;
+}
+
+/* Prepare the Urls for usage...
+ */
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * descURL, unsigned int scope_id)
+{
+ /* strdup descURL */
+ urls->rootdescURL = strdup(descURL);
+
+ /* get description of WANIPConnection */
+ urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
+ data->first.scpdurl, scope_id);
+ urls->controlURL = build_absolute_url(data->urlbase, descURL,
+ data->first.controlurl, scope_id);
+ urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
+ data->CIF.controlurl, scope_id);
+ urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
+ data->IPv6FC.controlurl, scope_id);
+
+#ifdef DEBUG
+ printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
+ printf("urls->controlURL='%s'\n", urls->controlURL);
+ printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
+ printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
+#endif
+}
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls * urls)
+{
+ if(!urls)
+ return;
+ free(urls->controlURL);
+ urls->controlURL = 0;
+ free(urls->ipcondescURL);
+ urls->ipcondescURL = 0;
+ free(urls->controlURL_CIF);
+ urls->controlURL_CIF = 0;
+ free(urls->controlURL_6FC);
+ urls->controlURL_6FC = 0;
+ free(urls->rootdescURL);
+ urls->rootdescURL = 0;
+}
+
+int
+UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ char status[64];
+ unsigned int uptime;
+ status[0] = '\0';
+ UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, NULL);
+ if(0 == strcmp("Connected", status))
+ return 1;
+ else if(0 == strcmp("Up", status)) /* Also accept "Up" */
+ return 1;
+ else
+ return 0;
+}
+
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * -1 = Internal error
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any positive non zero return case, the urls and data structures
+ * passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ struct xml_desc {
+ char * xml;
+ int size;
+ int is_igd;
+ } * desc = NULL;
+ struct UPNPDev * dev;
+ int ndev = 0;
+ int i;
+ int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
+ int n_igd = 0;
+ char extIpAddr[16];
+ char myLanAddr[40];
+ int status_code = -1;
+
+ if(!devlist)
+ {
+#ifdef DEBUG
+ printf("Empty devlist\n");
+#endif
+ return 0;
+ }
+ /* counting total number of devices in the list */
+ for(dev = devlist; dev; dev = dev->pNext)
+ ndev++;
+ if(ndev > 0)
+ {
+ desc = calloc(ndev, sizeof(struct xml_desc));
+ if(!desc)
+ return -1; /* memory allocation error */
+ }
+ /* Step 1 : downloading descriptions and testing type */
+ for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
+ {
+ /* we should choose an internet gateway device.
+ * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
+ desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
+ myLanAddr, sizeof(myLanAddr),
+ dev->scope_id, &status_code);
+#ifdef DEBUG
+ if(!desc[i].xml)
+ {
+ printf("error getting XML description %s\n", dev->descURL);
+ }
+#endif
+ if(desc[i].xml)
+ {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(desc[i].xml, desc[i].size, data);
+ if(COMPARE(data->CIF.servicetype,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
+ {
+ desc[i].is_igd = 1;
+ n_igd++;
+ if(lanaddr)
+ strncpy(lanaddr, myLanAddr, lanaddrlen);
+ }
+ }
+ }
+ /* iterate the list to find a device depending on state */
+ for(state = 1; state <= 3; state++)
+ {
+ for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
+ {
+ if(desc[i].xml)
+ {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(desc[i].xml, desc[i].size, data);
+ if(desc[i].is_igd || state >= 3 )
+ {
+ int is_connected;
+
+ GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
+
+ /* in state 2 and 3 we don't test if device is connected ! */
+ if(state >= 2)
+ goto free_and_return;
+ is_connected = UPNPIGD_IsConnected(urls, data);
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL, is_connected);
+#endif
+ /* checks that status is connected AND there is a external IP address assigned */
+ if(is_connected &&
+ (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
+ if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
+ && (0 != strcmp(extIpAddr, "0.0.0.0")))
+ goto free_and_return;
+ }
+ FreeUPNPUrls(urls);
+ if(data->second.servicetype[0] != '\0') {
+#ifdef DEBUG
+ printf("We tried %s, now we try %s !\n",
+ data->first.servicetype, data->second.servicetype);
+#endif
+ /* swaping WANPPPConnection and WANIPConnection ! */
+ memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
+ memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
+ memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
+ GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
+ is_connected = UPNPIGD_IsConnected(urls, data);
+#ifdef DEBUG
+ printf("UPNPIGD_IsConnected(%s) = %d\n",
+ urls->controlURL, is_connected);
+#endif
+ if(is_connected &&
+ (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
+ if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
+ && (0 != strcmp(extIpAddr, "0.0.0.0")))
+ goto free_and_return;
+ }
+ FreeUPNPUrls(urls);
+ }
+ }
+ memset(data, 0, sizeof(struct IGDdatas));
+ }
+ }
+ }
+ state = 0;
+free_and_return:
+ if(desc) {
+ for(i = 0; i < ndev; i++) {
+ if(desc[i].xml) {
+ free(desc[i].xml);
+ }
+ }
+ free(desc);
+ }
+ return state;
+}
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen)
+{
+ char * descXML;
+ int descXMLsize = 0;
+
+ descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
+ lanaddr, lanaddrlen, 0, NULL);
+ if(descXML) {
+ memset(data, 0, sizeof(struct IGDdatas));
+ memset(urls, 0, sizeof(struct UPNPUrls));
+ parserootdesc(descXML, descXMLsize, data);
+ free(descXML);
+ descXML = NULL;
+ GetUPNPUrls(urls, data, rootdescurl, 0);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/thirdparty/miniupnpc/miniupnpc.def b/thirdparty/miniupnpc/miniupnpc.def
new file mode 100644
index 00000000000..60e0bbe4232
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc.def
@@ -0,0 +1,45 @@
+LIBRARY
+; miniupnpc library
+ miniupnpc
+
+EXPORTS
+; miniupnpc
+ upnpDiscover
+ freeUPNPDevlist
+ parserootdesc
+ UPNP_GetValidIGD
+ UPNP_GetIGDFromUrl
+ GetUPNPUrls
+ FreeUPNPUrls
+; miniwget
+ miniwget
+ miniwget_getaddr
+; upnpcommands
+ UPNP_GetTotalBytesSent
+ UPNP_GetTotalBytesReceived
+ UPNP_GetTotalPacketsSent
+ UPNP_GetTotalPacketsReceived
+ UPNP_GetStatusInfo
+ UPNP_GetConnectionTypeInfo
+ UPNP_GetExternalIPAddress
+ UPNP_GetLinkLayerMaxBitRates
+ UPNP_AddPortMapping
+ UPNP_AddAnyPortMapping
+ UPNP_DeletePortMapping
+ UPNP_DeletePortMappingRange
+ UPNP_GetPortMappingNumberOfEntries
+ UPNP_GetSpecificPortMappingEntry
+ UPNP_GetGenericPortMappingEntry
+ UPNP_GetListOfPortMappings
+ UPNP_AddPinhole
+ UPNP_CheckPinholeWorking
+ UPNP_UpdatePinhole
+ UPNP_GetPinholePackets
+ UPNP_DeletePinhole
+ UPNP_GetFirewallStatus
+ UPNP_GetOutboundPinholeTimeout
+; upnperrors
+ strupnperror
+; portlistingparse
+ ParsePortListing
+ FreePortListing
diff --git a/thirdparty/miniupnpc/miniupnpc.h b/thirdparty/miniupnpc/miniupnpc.h
new file mode 100644
index 00000000000..8ddc282bd1e
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc.h
@@ -0,0 +1,153 @@
+/* $Id: miniupnpc.h,v 1.53 2018/05/07 11:05:16 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINIUPNPC_H_INCLUDED
+#define MINIUPNPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "igd_desc_parse.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define UPNPDISCOVER_SUCCESS (0)
+#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+#define UPNPDISCOVER_SOCKET_ERROR (-101)
+#define UPNPDISCOVER_MEMORY_ERROR (-102)
+
+/* versions : */
+#define MINIUPNPC_VERSION "2.1"
+#define MINIUPNPC_API_VERSION 17
+
+/* Source port:
+ Using "1" as an alias for 1900 for backwards compatibility
+ (presuming one would have used that for the "sameport" parameter) */
+#define UPNP_LOCAL_PORT_ANY 0
+#define UPNP_LOCAL_PORT_SAME 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+char *
+simpleUPnPcommand(int, const char *, const char *,
+ const char *, struct UPNParg *,
+ int *);
+
+/* upnpDiscover()
+ * discover UPnP devices on the network.
+ * The discovered devices are returned as a chained list.
+ * It is up to the caller to free the list with freeUPNPDevlist().
+ * delay (in millisecond) is the maximum time for waiting any device
+ * response.
+ * If available, device list will be obtained from MiniSSDPd.
+ * Default path for minissdpd socket will be used if minissdpdsock argument
+ * is NULL.
+ * If multicastif is not NULL, it will be used instead of the default
+ * multicast interface for sending SSDP discover packets.
+ * If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
+ * from the source port 1900 (same as destination port), if set to
+ * UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
+ * be attempted as the source port.
+ * "searchalltypes" parameter is useful when searching several types,
+ * if 0, the discovery will stop with the first type returning results.
+ * TTL should default to 2. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* structure used to get fast access to urls
+ * controlURL: controlURL of the WANIPConnection
+ * ipcondescURL: url of the description of the WANIPConnection
+ * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
+ * controlURL_6FC: controlURL of the WANIPv6FirewallControl
+ */
+struct UPNPUrls {
+ char * controlURL;
+ char * ipcondescURL;
+ char * controlURL_CIF;
+ char * controlURL_6FC;
+ char * rootdescURL;
+};
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * When succeding, urls, data, and lanaddr arguments are set.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+MINIUPNP_LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
+ const char *, unsigned int);
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls *);
+
+/* return 0 or 1 */
+MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc/miniupnpc.h b/thirdparty/miniupnpc/miniupnpc/miniupnpc.h
new file mode 100644
index 00000000000..8ddc282bd1e
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc/miniupnpc.h
@@ -0,0 +1,153 @@
+/* $Id: miniupnpc.h,v 1.53 2018/05/07 11:05:16 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef MINIUPNPC_H_INCLUDED
+#define MINIUPNPC_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "igd_desc_parse.h"
+#include "upnpdev.h"
+
+/* error codes : */
+#define UPNPDISCOVER_SUCCESS (0)
+#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+#define UPNPDISCOVER_SOCKET_ERROR (-101)
+#define UPNPDISCOVER_MEMORY_ERROR (-102)
+
+/* versions : */
+#define MINIUPNPC_VERSION "2.1"
+#define MINIUPNPC_API_VERSION 17
+
+/* Source port:
+ Using "1" as an alias for 1900 for backwards compatibility
+ (presuming one would have used that for the "sameport" parameter) */
+#define UPNP_LOCAL_PORT_ANY 0
+#define UPNP_LOCAL_PORT_SAME 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+char *
+simpleUPnPcommand(int, const char *, const char *,
+ const char *, struct UPNParg *,
+ int *);
+
+/* upnpDiscover()
+ * discover UPnP devices on the network.
+ * The discovered devices are returned as a chained list.
+ * It is up to the caller to free the list with freeUPNPDevlist().
+ * delay (in millisecond) is the maximum time for waiting any device
+ * response.
+ * If available, device list will be obtained from MiniSSDPd.
+ * Default path for minissdpd socket will be used if minissdpdsock argument
+ * is NULL.
+ * If multicastif is not NULL, it will be used instead of the default
+ * multicast interface for sending SSDP discover packets.
+ * If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
+ * from the source port 1900 (same as destination port), if set to
+ * UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
+ * be attempted as the source port.
+ * "searchalltypes" parameter is useful when searching several types,
+ * if 0, the discovery will stop with the first type returning results.
+ * TTL should default to 2. */
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverAll(int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error);
+
+MINIUPNP_LIBSPEC struct UPNPDev *
+upnpDiscoverDevices(const char * const deviceTypes[],
+ int delay, const char * multicastif,
+ const char * minissdpdsock, int localport,
+ int ipv6, unsigned char ttl,
+ int * error,
+ int searchalltypes);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* structure used to get fast access to urls
+ * controlURL: controlURL of the WANIPConnection
+ * ipcondescURL: url of the description of the WANIPConnection
+ * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
+ * controlURL_6FC: controlURL of the WANIPv6FirewallControl
+ */
+struct UPNPUrls {
+ char * controlURL;
+ char * ipcondescURL;
+ char * controlURL_CIF;
+ char * controlURL_6FC;
+ char * rootdescURL;
+};
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ * 0 = NO IGD found
+ * 1 = A valid connected IGD has been found
+ * 2 = A valid IGD has been found but it reported as
+ * not connected
+ * 3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * When succeding, urls, data, and lanaddr arguments are set.
+ * return value :
+ * 0 - Not ok
+ * 1 - OK */
+MINIUPNP_LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+ struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ char * lanaddr, int lanaddrlen);
+
+MINIUPNP_LIBSPEC void
+GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
+ const char *, unsigned int);
+
+MINIUPNP_LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls *);
+
+/* return 0 or 1 */
+MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc/miniwget.h b/thirdparty/miniupnpc/miniupnpc/miniwget.h
new file mode 100644
index 00000000000..f5572c25449
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc/miniwget.h
@@ -0,0 +1,27 @@
+/* $Id: miniwget.h,v 1.12 2016/01/24 17:24:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIWGET_H_INCLUDED
+#define MINIWGET_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *);
+
+MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *);
+
+int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/miniupnpc/upnpcommands.h b/thirdparty/miniupnpc/miniupnpc/upnpcommands.h
new file mode 100644
index 00000000000..0c6d501666a
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc/upnpcommands.h
@@ -0,0 +1,348 @@
+/* $Id: upnpcommands.h,v 1.32 2018/03/13 23:34:47 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef UPNPCOMMANDS_H_INCLUDED
+#define UPNPCOMMANDS_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "miniupnpctypes.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+#define UPNPCOMMAND_HTTP_ERROR (-3)
+#define UPNPCOMMAND_INVALID_RESPONSE (-4)
+#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct PortMappingParserData;
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType);
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control. */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+ const char* servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp);
+
+/* UPNP_AddPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ * with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ * must be the same
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ * permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ * and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ * cannot be a specific port value
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration);
+
+/* UPNP_AddAnyPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort);
+
+/* UPNP_DeletePortMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost);
+
+/* UPNP_DeletePortRangeMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 730 PortMappingNotFound - This error message is returned if no port
+ * mapping is found in the specified range.
+ * 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
+ const char* servicetype,
+ unsigned int * num);
+
+/* UPNP_GetSpecificPortMappingEntry()
+ * retrieves an existing port mapping
+ * params :
+ * in extPort
+ * in proto
+ * in remoteHost
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out leaseDuration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * List of possible UPnP errors for _GetSpecificPortMappingEntry :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration);
+
+/* UPNP_GetGenericPortMappingEntry()
+ * params :
+ * in index
+ * out extPort (6 bytes)
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out protocol (4 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out rHost (64 bytes)
+ * out duration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * Possible UPNP Error codes :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration);
+
+/* UPNP_GetListOfPortMappings() Available in IGD v2
+ *
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data);
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout);
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime);
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniupnpc_declspec.h b/thirdparty/miniupnpc/miniupnpc_declspec.h
new file mode 100644
index 00000000000..40adb922ec3
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc_declspec.h
@@ -0,0 +1,21 @@
+#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED
+#define MINIUPNPC_DECLSPEC_H_INCLUDED
+
+#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB)
+ /* for windows dll */
+ #ifdef MINIUPNP_EXPORTS
+ #define MINIUPNP_LIBSPEC __declspec(dllexport)
+ #else
+ #define MINIUPNP_LIBSPEC __declspec(dllimport)
+ #endif
+#else
+ #if defined(__GNUC__) && __GNUC__ >= 4
+ /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
+ #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
+ #else
+ #define MINIUPNP_LIBSPEC
+ #endif
+#endif
+
+#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */
+
diff --git a/thirdparty/miniupnpc/miniupnpc_socketdef.h b/thirdparty/miniupnpc/miniupnpc_socketdef.h
new file mode 100644
index 00000000000..965d9151b9e
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpc_socketdef.h
@@ -0,0 +1,37 @@
+/* $Id: miniupnpc_socketdef.h,v 1.1 2018/03/13 23:44:10 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+ * Copyright (c) 2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED
+#define MINIUPNPC_SOCKETDEF_H_INCLUDED
+
+#ifdef _MSC_VER
+
+#define ISINVALID(s) (INVALID_SOCKET==(s))
+
+#else
+
+#ifndef SOCKET
+#define SOCKET int
+#endif
+#ifndef SSIZE_T
+#define SSIZE_T ssize_t
+#endif
+#ifndef INVALID_SOCKET
+#define INVALID_SOCKET (-1)
+#endif
+#ifndef ISINVALID
+#define ISINVALID(s) ((s)<0)
+#endif
+
+#endif
+
+#ifdef _WIN32
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#endif /* MINIUPNPC_SOCKETDEF_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/miniupnpcmodule.c b/thirdparty/miniupnpc/miniupnpcmodule.c
new file mode 100644
index 00000000000..8657a0e0027
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpcmodule.c
@@ -0,0 +1,703 @@
+/* $Id: miniupnpcmodule.c,v 1.24 2014/06/10 09:48:11 nanard Exp $*/
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * website : https://miniupnp.tuxfamily.org/
+ * copyright (c) 2007-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include
+#define MINIUPNP_STATICLIB
+#include "structmember.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "upnperrors.h"
+
+#ifdef _WIN32
+#include
+#endif
+
+/* for compatibility with Python < 2.4 */
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+#ifndef Py_RETURN_TRUE
+#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
+#endif
+
+#ifndef Py_RETURN_FALSE
+#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#endif
+
+/* for compatibility with Python < 3.0 */
+#ifndef PyVarObject_HEAD_INIT
+#define PyVarObject_HEAD_INIT(type, size) \
+ PyObject_HEAD_INIT(type) size,
+#endif
+
+#ifndef Py_TYPE
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ /* Type-specific fields go here. */
+ struct UPNPDev * devlist;
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ unsigned int discoverdelay; /* value passed to upnpDiscover() */
+ unsigned int localport; /* value passed to upnpDiscover() */
+ char lanaddr[40]; /* our ip address on the LAN */
+ char * multicastif;
+ char * minissdpdsocket;
+} UPnPObject;
+
+static PyMemberDef UPnP_members[] = {
+ {"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
+ READONLY, "ip address on the LAN"
+ },
+ {"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
+ 0/*READWRITE*/, "value in ms used to wait for SSDP responses"
+ },
+ {"localport", T_UINT, offsetof(UPnPObject, localport),
+ 0/*READWRITE*/,
+ "If localport is set to UPNP_LOCAL_PORT_SAME(1) "
+ "SSDP packets will be sent from the source port "
+ "1900 (same as destination port), if set to "
+ "UPNP_LOCAL_PORT_ANY(0) system assign a source "
+ "port, any other value will be attempted as the "
+ "source port"
+ },
+ /* T_STRING is allways readonly :( */
+ {"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
+ 0, "IP of the network interface to be used for multicast operations"
+ },
+ {"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket),
+ 0, "path of the MiniSSDPd unix socket"
+ },
+ {NULL}
+};
+
+
+static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds)
+{
+ char* multicastif = NULL;
+ char* minissdpdsocket = NULL;
+ static char *kwlist[] = {
+ "multicastif", "minissdpdsocket", "discoverdelay",
+ "localport", NULL
+ };
+
+ if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist,
+ &multicastif,
+ &minissdpdsocket,
+ &self->discoverdelay,
+ &self->localport))
+ return -1;
+
+ if(self->localport>1 &&
+ (self->localport>65534||self->localport<1024)) {
+ PyErr_SetString(PyExc_Exception, "Invalid localport value");
+ return -1;
+ }
+ if(multicastif)
+ self->multicastif = strdup(multicastif);
+ if(minissdpdsocket)
+ self->minissdpdsocket = strdup(minissdpdsocket);
+
+ return 0;
+}
+
+static void
+UPnPObject_dealloc(UPnPObject *self)
+{
+ freeUPNPDevlist(self->devlist);
+ FreeUPNPUrls(&self->urls);
+ free(self->multicastif);
+ free(self->minissdpdsocket);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+static PyObject *
+UPnP_discover(UPnPObject *self)
+{
+ struct UPNPDev * dev;
+ int i;
+ PyObject *res = NULL;
+ if(self->devlist)
+ {
+ freeUPNPDevlist(self->devlist);
+ self->devlist = 0;
+ }
+ Py_BEGIN_ALLOW_THREADS
+ self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
+ self->multicastif,
+ self->minissdpdsocket,
+ (int)self->localport,
+ 0/*ip v6*/,
+ 2/* TTL */,
+ 0/*error */);
+ Py_END_ALLOW_THREADS
+ /* Py_RETURN_NONE ??? */
+ for(dev = self->devlist, i = 0; dev; dev = dev->pNext)
+ i++;
+ res = Py_BuildValue("i", i);
+ return res;
+}
+
+static PyObject *
+UPnP_selectigd(UPnPObject *self)
+{
+ int r;
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
+ self->lanaddr, sizeof(self->lanaddr));
+Py_END_ALLOW_THREADS
+ if(r)
+ {
+ return Py_BuildValue("s", self->urls.controlURL);
+ }
+ else
+ {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_totalbytesent(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalbytereceived(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalpacketsent(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_totalpacketreceived(UPnPObject *self)
+{
+ UNSIGNED_INTEGER i;
+Py_BEGIN_ALLOW_THREADS
+ i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
+ self->data.CIF.servicetype);
+Py_END_ALLOW_THREADS
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", i);
+#else
+ return Py_BuildValue("i", (int)i);
+#endif
+}
+
+static PyObject *
+UPnP_statusinfo(UPnPObject *self)
+{
+ char status[64];
+ char lastconnerror[64];
+ unsigned int uptime = 0;
+ int r;
+ status[0] = '\0';
+ lastconnerror[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
+ status, &uptime, lastconnerror);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
+#else
+ return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
+#endif
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_connectiontype(UPnPObject *self)
+{
+ char connectionType[64];
+ int r;
+ connectionType[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
+ self->data.first.servicetype,
+ connectionType);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("s", connectionType);
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_externalipaddress(UPnPObject *self)
+{
+ char externalIPAddress[40];
+ int r;
+ externalIPAddress[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetExternalIPAddress(self->urls.controlURL,
+ self->data.first.servicetype,
+ externalIPAddress);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("s", externalIPAddress);
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
+ * remoteHost)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+UPnP_addportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ char inPort[6];
+ unsigned short iPort;
+ const char * proto;
+ const char * host;
+ const char * desc;
+ const char * remoteHost;
+ const char * leaseDuration = "0";
+ int r;
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto,
+ &host, &iPort, &desc, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ sprintf(inPort, "%hu", iPort);
+ r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, inPort, host, desc, proto,
+ remoteHost, leaseDuration);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS)
+ {
+ Py_RETURN_TRUE;
+ }
+ else
+ {
+ // TODO: RAISE an Exception. See upnpcommands.h for errors codes.
+ // upnperrors.c
+ //Py_RETURN_FALSE;
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
+ * remoteHost)
+ * protocol is 'UDP' or 'TCP' */
+static PyObject *
+UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ char inPort[6];
+ unsigned short iPort;
+ char reservedPort[6];
+ const char * proto;
+ const char * host;
+ const char * desc;
+ const char * remoteHost;
+ const char * leaseDuration = "0";
+ int r;
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ sprintf(inPort, "%hu", iPort);
+ r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, inPort, host, desc, proto,
+ remoteHost, leaseDuration, reservedPort);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ return Py_BuildValue("i", atoi(reservedPort));
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+
+/* DeletePortMapping(extPort, proto, removeHost='')
+ * proto = 'UDP', 'TCP' */
+static PyObject *
+UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ const char * proto;
+ const char * remoteHost = "";
+ int r;
+ if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
+ extPort, proto, remoteHost);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ Py_RETURN_TRUE;
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* DeletePortMappingRange(extPort, proto, removeHost='')
+ * proto = 'UDP', 'TCP' */
+static PyObject *
+UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
+{
+ char extPortStart[6];
+ unsigned short ePortStart;
+ char extPortEnd[6];
+ unsigned short ePortEnd;
+ const char * proto;
+ unsigned char manage;
+ char manageStr[6];
+ int r;
+ if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPortStart, "%hu", ePortStart);
+ sprintf(extPortEnd, "%hu", ePortEnd);
+ sprintf(manageStr, "%hu", (unsigned short)manage);
+ r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
+ extPortStart, extPortEnd, proto, manageStr);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+ Py_RETURN_TRUE;
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+static PyObject *
+UPnP_getportmappingnumberofentries(UPnPObject *self)
+{
+ unsigned int n = 0;
+ int r;
+Py_BEGIN_ALLOW_THREADS
+ r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
+ self->data.first.servicetype,
+ &n);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS) {
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("I", n);
+#else
+ return Py_BuildValue("i", (int)n);
+#endif
+ } else {
+ /* TODO: have our own exception type ! */
+ PyErr_SetString(PyExc_Exception, strupnperror(r));
+ return NULL;
+ }
+}
+
+/* GetSpecificPortMapping(ePort, proto, remoteHost='')
+ * proto = 'UDP' or 'TCP' */
+static PyObject *
+UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
+{
+ char extPort[6];
+ unsigned short ePort;
+ const char * proto;
+ const char * remoteHost = "";
+ char intClient[40];
+ char intPort[6];
+ unsigned short iPort;
+ char desc[80];
+ char enabled[4];
+ char leaseDuration[16];
+ if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
+ return NULL;
+ extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
+ desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
+Py_BEGIN_ALLOW_THREADS
+ sprintf(extPort, "%hu", ePort);
+ UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
+ self->data.first.servicetype,
+ extPort, proto, remoteHost,
+ intClient, intPort,
+ desc, enabled, leaseDuration);
+Py_END_ALLOW_THREADS
+ if(intClient[0])
+ {
+ iPort = (unsigned short)atoi(intPort);
+ return Py_BuildValue("(s,H,s,O,i)",
+ intClient, iPort, desc,
+ PyBool_FromLong(atoi(enabled)),
+ atoi(leaseDuration));
+ }
+ else
+ {
+ Py_RETURN_NONE;
+ }
+}
+
+/* GetGenericPortMapping(index) */
+static PyObject *
+UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
+{
+ int i, r;
+ char index[8];
+ char intClient[40];
+ char intPort[6];
+ unsigned short iPort;
+ char extPort[6];
+ unsigned short ePort;
+ char protocol[4];
+ char desc[80];
+ char enabled[6];
+ char rHost[64];
+ char duration[16]; /* lease duration */
+ unsigned int dur;
+ if(!PyArg_ParseTuple(args, "i", &i))
+ return NULL;
+Py_BEGIN_ALLOW_THREADS
+ snprintf(index, sizeof(index), "%d", i);
+ rHost[0] = '\0'; enabled[0] = '\0';
+ duration[0] = '\0'; desc[0] = '\0';
+ extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+ r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
+ self->data.first.servicetype,
+ index,
+ extPort, intClient, intPort,
+ protocol, desc, enabled, rHost,
+ duration);
+Py_END_ALLOW_THREADS
+ if(r==UPNPCOMMAND_SUCCESS)
+ {
+ ePort = (unsigned short)atoi(extPort);
+ iPort = (unsigned short)atoi(intPort);
+ dur = (unsigned int)strtoul(duration, 0, 0);
+#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
+ return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
+ ePort, protocol, intClient, iPort,
+ desc, enabled, rHost, dur);
+#else
+ return Py_BuildValue("(i,s,(s,i),s,s,s,i)",
+ (int)ePort, protocol, intClient, (int)iPort,
+ desc, enabled, rHost, (int)dur);
+#endif
+ }
+ else
+ {
+ Py_RETURN_NONE;
+ }
+}
+
+/* miniupnpc.UPnP object Method Table */
+static PyMethodDef UPnP_methods[] = {
+ {"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
+ "discover UPnP IGD devices on the network"
+ },
+ {"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS,
+ "select a valid UPnP IGD among discovered devices"
+ },
+ {"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
+ "return the total number of bytes sent by UPnP IGD"
+ },
+ {"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
+ "return the total number of bytes received by UPnP IGD"
+ },
+ {"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
+ "return the total number of packets sent by UPnP IGD"
+ },
+ {"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
+ "return the total number of packets received by UPnP IGD"
+ },
+ {"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
+ "return status and uptime"
+ },
+ {"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
+ "return IGD WAN connection type"
+ },
+ {"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
+ "return external IP address"
+ },
+ {"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
+ "add a port mapping"
+ },
+ {"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
+ "add a port mapping, IGD to select alternative if necessary"
+ },
+ {"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
+ "delete a port mapping"
+ },
+ {"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
+ "delete a range of port mappings"
+ },
+ {"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
+ "-- non standard --"
+ },
+ {"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
+ "get details about a specific port mapping entry"
+ },
+ {"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
+ "get all details about the port mapping at index"
+ },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject UPnPType = {
+ PyVarObject_HEAD_INIT(NULL,
+ 0) /*ob_size*/
+ "miniupnpc.UPnP", /*tp_name*/
+ sizeof(UPnPObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)UPnPObject_dealloc,/*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "UPnP objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ UPnP_methods, /* tp_methods */
+ UPnP_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)UPnP_init, /* tp_init */
+ 0, /* tp_alloc */
+#ifndef _WIN32
+ PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
+#else
+ 0,
+#endif
+};
+
+/* module methods */
+static PyMethodDef miniupnpc_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "miniupnpc", /* m_name */
+ "miniupnpc module.", /* m_doc */
+ -1, /* m_size */
+ miniupnpc_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+#endif
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+
+PyMODINIT_FUNC
+#if PY_MAJOR_VERSION >= 3
+PyInit_miniupnpc(void)
+#else
+initminiupnpc(void)
+#endif
+{
+ PyObject* m;
+
+#ifdef _WIN32
+ /* initialize Winsock. */
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+
+ UPnPType.tp_new = PyType_GenericNew;
+#endif
+ if (PyType_Ready(&UPnPType) < 0)
+#if PY_MAJOR_VERSION >= 3
+ return 0;
+#else
+ return;
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+ m = PyModule_Create(&moduledef);
+#else
+ m = Py_InitModule3("miniupnpc", miniupnpc_methods,
+ "miniupnpc module.");
+#endif
+
+ Py_INCREF(&UPnPType);
+ PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
+
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
+}
+
diff --git a/thirdparty/miniupnpc/miniupnpcstrings.h b/thirdparty/miniupnpc/miniupnpcstrings.h
new file mode 100644
index 00000000000..1d5c5882fd3
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpcstrings.h
@@ -0,0 +1,17 @@
+#ifndef MINIUPNPCSTRINGS_H_INCLUDED
+#define MINIUPNPCSTRINGS_H_INCLUDED
+
+#include
+
+#define OS_STRING VERSION_NAME "/1.0"
+#define MINIUPNPC_VERSION_STRING "2.1"
+
+#if 0
+/* according to "UPnP Device Architecture 1.0" */
+#define UPNP_VERSION_STRING "UPnP/1.0"
+#else
+/* according to "UPnP Device Architecture 1.1" */
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/miniupnpctypes.h b/thirdparty/miniupnpc/miniupnpctypes.h
new file mode 100644
index 00000000000..307ce396990
--- /dev/null
+++ b/thirdparty/miniupnpc/miniupnpctypes.h
@@ -0,0 +1,19 @@
+/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
+ * Author : Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef MINIUPNPCTYPES_H_INCLUDED
+#define MINIUPNPCTYPES_H_INCLUDED
+
+#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+#define UNSIGNED_INTEGER unsigned long long
+#define STRTOUI strtoull
+#else
+#define UNSIGNED_INTEGER unsigned int
+#define STRTOUI strtoul
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/miniwget.c b/thirdparty/miniupnpc/miniwget.c
new file mode 100644
index 00000000000..a46ba760224
--- /dev/null
+++ b/thirdparty/miniupnpc/miniwget.c
@@ -0,0 +1,662 @@
+/* $Id: miniwget.c,v 1.78 2018/03/13 23:22:18 nanard Exp $ */
+/* Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#include
+#include
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define socklen_t int
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#else /* #ifdef _WIN32 */
+#include
+#include
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include
+#include
+#include
+#include
+#include
+#define closesocket close
+#include
+#endif /* #else _WIN32 */
+#ifdef __GNU__
+#define MAXHOSTNAMELEN 64
+#endif /* __GNU__ */
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif /* MIN */
+
+
+#include "miniupnpcstrings.h"
+#include "miniwget.h"
+#include "connecthostport.h"
+#include "receivedata.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/*
+ * Read a HTTP response from a socket.
+ * Process Content-Length and Transfer-encoding headers.
+ * return a pointer to the content buffer, which length is saved
+ * to the length parameter.
+ */
+void *
+getHTTPResponse(SOCKET s, int * size, int * status_code)
+{
+ char buf[2048];
+ int n;
+ int endofheaders = 0;
+ int chunked = 0;
+ int content_length = -1;
+ unsigned int chunksize = 0;
+ unsigned int bytestocopy = 0;
+ /* buffers : */
+ char * header_buf;
+ unsigned int header_buf_len = 2048;
+ unsigned int header_buf_used = 0;
+ char * content_buf;
+ unsigned int content_buf_len = 2048;
+ unsigned int content_buf_used = 0;
+ char chunksize_buf[32];
+ unsigned int chunksize_buf_index;
+#ifdef DEBUG
+ char * reason_phrase = NULL;
+ int reason_phrase_len = 0;
+#endif
+
+ if(status_code) *status_code = -1;
+ header_buf = malloc(header_buf_len);
+ if(header_buf == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
+#endif /* DEBUG */
+ *size = -1;
+ return NULL;
+ }
+ content_buf = malloc(content_buf_len);
+ if(content_buf == NULL)
+ {
+ free(header_buf);
+#ifdef DEBUG
+ fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
+#endif /* DEBUG */
+ *size = -1;
+ return NULL;
+ }
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+
+ while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
+ {
+ if(endofheaders == 0)
+ {
+ int i;
+ int linestart=0;
+ int colon=0;
+ int valuestart=0;
+ if(header_buf_used + n > header_buf_len) {
+ char * tmp = realloc(header_buf, header_buf_used + n);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(header_buf);
+ free(content_buf);
+ *size = -1;
+ return NULL;
+ }
+ header_buf = tmp;
+ header_buf_len = header_buf_used + n;
+ }
+ memcpy(header_buf + header_buf_used, buf, n);
+ header_buf_used += n;
+ /* search for CR LF CR LF (end of headers)
+ * recognize also LF LF */
+ i = 0;
+ while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
+ if(header_buf[i] == '\r') {
+ i++;
+ if(header_buf[i] == '\n') {
+ i++;
+ if(i < (int)header_buf_used && header_buf[i] == '\r') {
+ i++;
+ if(i < (int)header_buf_used && header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ }
+ } else if(header_buf[i] == '\n') {
+ i++;
+ if(header_buf[i] == '\n') {
+ endofheaders = i+1;
+ }
+ }
+ i++;
+ }
+ if(endofheaders == 0)
+ continue;
+ /* parse header lines */
+ for(i = 0; i < endofheaders - 1; i++) {
+ if(linestart > 0 && colon <= linestart && header_buf[i]==':')
+ {
+ colon = i;
+ while(i < (endofheaders-1)
+ && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
+ i++;
+ valuestart = i + 1;
+ }
+ /* detecting end of line */
+ else if(header_buf[i]=='\r' || header_buf[i]=='\n')
+ {
+ if(linestart == 0 && status_code)
+ {
+ /* Status line
+ * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
+ int sp;
+ for(sp = 0; sp < i; sp++)
+ if(header_buf[sp] == ' ')
+ {
+ if(*status_code < 0)
+ *status_code = atoi(header_buf + sp + 1);
+ else
+ {
+#ifdef DEBUG
+ reason_phrase = header_buf + sp + 1;
+ reason_phrase_len = i - sp - 1;
+#endif
+ break;
+ }
+ }
+#ifdef DEBUG
+ printf("HTTP status code = %d, Reason phrase = %.*s\n",
+ *status_code, reason_phrase_len, reason_phrase);
+#endif
+ }
+ else if(colon > linestart && valuestart > colon)
+ {
+#ifdef DEBUG
+ printf("header='%.*s', value='%.*s'\n",
+ colon-linestart, header_buf+linestart,
+ i-valuestart, header_buf+valuestart);
+#endif
+ if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
+ {
+ content_length = atoi(header_buf+valuestart);
+#ifdef DEBUG
+ printf("Content-Length: %d\n", content_length);
+#endif
+ }
+ else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
+ && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
+ {
+#ifdef DEBUG
+ printf("chunked transfer-encoding!\n");
+#endif
+ chunked = 1;
+ }
+ }
+ while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
+ i++;
+ linestart = i;
+ colon = linestart;
+ valuestart = 0;
+ }
+ }
+ /* copy the remaining of the received data back to buf */
+ n = header_buf_used - endofheaders;
+ memcpy(buf, header_buf + endofheaders, n);
+ /* if(headers) */
+ }
+ /* if we get there, endofheaders != 0.
+ * In the other case, there was a continue above */
+ /* content */
+ if(chunked)
+ {
+ int i = 0;
+ while(i < n)
+ {
+ if(chunksize == 0)
+ {
+ /* reading chunk size */
+ if(chunksize_buf_index == 0) {
+ /* skipping any leading CR LF */
+ if(i= '0'
+ && chunksize_buf[j] <= '9')
+ chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
+ else
+ chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
+ }
+ chunksize_buf[0] = '\0';
+ chunksize_buf_index = 0;
+ i++;
+ } else {
+ /* not finished to get chunksize */
+ continue;
+ }
+#ifdef DEBUG
+ printf("chunksize = %u (%x)\n", chunksize, chunksize);
+#endif
+ if(chunksize == 0)
+ {
+#ifdef DEBUG
+ printf("end of HTTP content - %d %d\n", i, n);
+ /*printf("'%.*s'\n", n-i, buf+i);*/
+#endif
+ goto end_of_stream;
+ }
+ }
+ /* it is guaranteed that (n >= i) */
+ bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
+ if((content_buf_used + bytestocopy) > content_buf_len)
+ {
+ char * tmp;
+ if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + bytestocopy;
+ }
+ tmp = realloc(content_buf, content_buf_len);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(content_buf);
+ free(header_buf);
+ *size = -1;
+ return NULL;
+ }
+ content_buf = tmp;
+ }
+ memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
+ content_buf_used += bytestocopy;
+ i += bytestocopy;
+ chunksize -= bytestocopy;
+ }
+ }
+ else
+ {
+ /* not chunked */
+ if(content_length > 0
+ && (content_buf_used + n) > (unsigned int)content_length) {
+ /* skipping additional bytes */
+ n = content_length - content_buf_used;
+ }
+ if(content_buf_used + n > content_buf_len)
+ {
+ char * tmp;
+ if(content_length >= 0
+ && (unsigned int)content_length >= (content_buf_used + n)) {
+ content_buf_len = content_length;
+ } else {
+ content_buf_len = content_buf_used + n;
+ }
+ tmp = realloc(content_buf, content_buf_len);
+ if(tmp == NULL) {
+ /* memory allocation error */
+ free(content_buf);
+ free(header_buf);
+ *size = -1;
+ return NULL;
+ }
+ content_buf = tmp;
+ }
+ memcpy(content_buf + content_buf_used, buf, n);
+ content_buf_used += n;
+ }
+ /* use the Content-Length header value if available */
+ if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
+ {
+#ifdef DEBUG
+ printf("End of HTTP content\n");
+#endif
+ break;
+ }
+ }
+end_of_stream:
+ free(header_buf); header_buf = NULL;
+ *size = content_buf_used;
+ if(content_buf_used == 0)
+ {
+ free(content_buf);
+ content_buf = NULL;
+ }
+ return content_buf;
+}
+
+/* miniwget3() :
+ * do all the work.
+ * Return NULL if something failed. */
+static void *
+miniwget3(const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ const char * httpversion, unsigned int scope_id,
+ int * status_code)
+{
+ char buf[2048];
+ SOCKET s;
+ int n;
+ int len;
+ int sent;
+ void * content;
+
+ *size = 0;
+ s = connecthostport(host, port, scope_id);
+ if(ISINVALID(s))
+ return NULL;
+
+ /* get address for caller ! */
+ if(addr_str)
+ {
+ struct sockaddr_storage saddr;
+ socklen_t saddrlen;
+
+ saddrlen = sizeof(saddr);
+ if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
+ {
+ perror("getsockname");
+ }
+ else
+ {
+#if defined(__amigaos__) && !defined(__amigaos4__)
+ /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
+ * But his function make a string with the port : nn.nn.nn.nn:port */
+/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
+ NULL, addr_str, (DWORD *)&addr_str_len))
+ {
+ printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
+ }*/
+ /* the following code is only compatible with ip v4 addresses */
+ strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
+#else
+#if 0
+ if(saddr.sa_family == AF_INET6) {
+ inet_ntop(AF_INET6,
+ &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
+ addr_str, addr_str_len);
+ } else {
+ inet_ntop(AF_INET,
+ &(((struct sockaddr_in *)&saddr)->sin_addr),
+ addr_str, addr_str_len);
+ }
+#endif
+ /* getnameinfo return ip v6 address with the scope identifier
+ * such as : 2a01:e35:8b2b:7330::%4281128194 */
+ n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
+ addr_str, addr_str_len,
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if(n != 0) {
+#ifdef _WIN32
+ fprintf(stderr, "getnameinfo() failed : %d\n", n);
+#else
+ fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
+#endif
+ }
+#endif
+ }
+#ifdef DEBUG
+ printf("address miniwget : %s\n", addr_str);
+#endif
+ }
+
+ len = snprintf(buf, sizeof(buf),
+ "GET %s HTTP/%s\r\n"
+ "Host: %s:%d\r\n"
+ "Connection: Close\r\n"
+ "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+
+ "\r\n",
+ path, httpversion, host, port);
+ if ((unsigned int)len >= sizeof(buf))
+ {
+ closesocket(s);
+ return NULL;
+ }
+ sent = 0;
+ /* sending the HTTP request */
+ while(sent < len)
+ {
+ n = send(s, buf+sent, len-sent, 0);
+ if(n < 0)
+ {
+ perror("send");
+ closesocket(s);
+ return NULL;
+ }
+ else
+ {
+ sent += n;
+ }
+ }
+ content = getHTTPResponse(s, size, status_code);
+ closesocket(s);
+ return content;
+}
+
+/* miniwget2() :
+ * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
+static void *
+miniwget2(const char * host,
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ unsigned int scope_id, int * status_code)
+{
+ char * respbuffer;
+
+#if 1
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.1",
+ scope_id, status_code);
+#else
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.0",
+ scope_id, status_code);
+ if (*size == 0)
+ {
+#ifdef DEBUG
+ printf("Retrying with HTTP/1.1\n");
+#endif
+ free(respbuffer);
+ respbuffer = miniwget3(host, port, path, size,
+ addr_str, addr_str_len, "1.1",
+ scope_id, status_code);
+ }
+#endif
+ return respbuffer;
+}
+
+
+
+
+/* parseURL()
+ * arguments :
+ * url : source string not modified
+ * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
+ * port : port (destination)
+ * path : pointer to the path part of the URL
+ *
+ * Return values :
+ * 0 - Failure
+ * 1 - Success */
+int
+parseURL(const char * url,
+ char * hostname, unsigned short * port,
+ char * * path, unsigned int * scope_id)
+{
+ char * p1, *p2, *p3;
+ if(!url)
+ return 0;
+ p1 = strstr(url, "://");
+ if(!p1)
+ return 0;
+ p1 += 3;
+ if( (url[0]!='h') || (url[1]!='t')
+ ||(url[2]!='t') || (url[3]!='p'))
+ return 0;
+ memset(hostname, 0, MAXHOSTNAMELEN + 1);
+ if(*p1 == '[')
+ {
+ /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
+ char * scope;
+ scope = strchr(p1, '%');
+ p2 = strchr(p1, ']');
+ if(p2 && scope && scope < p2 && scope_id) {
+ /* parse scope */
+#ifdef IF_NAMESIZE
+ char tmp[IF_NAMESIZE];
+ int l;
+ scope++;
+ /* "%25" is just '%' in URL encoding */
+ if(scope[0] == '2' && scope[1] == '5')
+ scope += 2; /* skip "25" */
+ l = p2 - scope;
+ if(l >= IF_NAMESIZE)
+ l = IF_NAMESIZE - 1;
+ memcpy(tmp, scope, l);
+ tmp[l] = '\0';
+ *scope_id = if_nametoindex(tmp);
+ if(*scope_id == 0) {
+ *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
+ }
+#else
+ /* under windows, scope is numerical */
+ char tmp[8];
+ int l;
+ scope++;
+ /* "%25" is just '%' in URL encoding */
+ if(scope[0] == '2' && scope[1] == '5')
+ scope += 2; /* skip "25" */
+ l = p2 - scope;
+ if(l >= sizeof(tmp))
+ l = sizeof(tmp) - 1;
+ memcpy(tmp, scope, l);
+ tmp[l] = '\0';
+ *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
+#endif
+ }
+ p3 = strchr(p1, '/');
+ if(p2 && p3)
+ {
+ p2++;
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ if(*p2 == ':')
+ {
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ else
+ {
+ *port = 80;
+ }
+ *path = p3;
+ return 1;
+ }
+ }
+ p2 = strchr(p1, ':');
+ p3 = strchr(p1, '/');
+ if(!p3)
+ return 0;
+ if(!p2 || (p2>p3))
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
+ *port = 80;
+ }
+ else
+ {
+ strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+ *port = 0;
+ p2++;
+ while( (*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short)(*p2 - '0');
+ p2++;
+ }
+ }
+ *path = p3;
+ return 1;
+}
+
+void *
+miniwget(const char * url, int * size,
+ unsigned int scope_id, int * status_code)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/chemin */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(!parseURL(url, hostname, &port, &path, &scope_id))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
+ hostname, port, path, scope_id);
+#endif
+ return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code);
+}
+
+void *
+miniwget_getaddr(const char * url, int * size,
+ char * addr, int addrlen, unsigned int scope_id,
+ int * status_code)
+{
+ unsigned short port;
+ char * path;
+ /* protocol://host:port/path */
+ char hostname[MAXHOSTNAMELEN+1];
+ *size = 0;
+ if(addr)
+ addr[0] = '\0';
+ if(!parseURL(url, hostname, &port, &path, &scope_id))
+ return NULL;
+#ifdef DEBUG
+ printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
+ hostname, port, path, scope_id);
+#endif
+ return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code);
+}
+
diff --git a/thirdparty/miniupnpc/miniwget.h b/thirdparty/miniupnpc/miniwget.h
new file mode 100644
index 00000000000..f5572c25449
--- /dev/null
+++ b/thirdparty/miniupnpc/miniwget.h
@@ -0,0 +1,27 @@
+/* $Id: miniwget.h,v 1.12 2016/01/24 17:24:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIWGET_H_INCLUDED
+#define MINIWGET_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *);
+
+MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *);
+
+int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/miniwget_private.h b/thirdparty/miniupnpc/miniwget_private.h
new file mode 100644
index 00000000000..e4eaac80851
--- /dev/null
+++ b/thirdparty/miniupnpc/miniwget_private.h
@@ -0,0 +1,15 @@
+/* $Id: miniwget_private.h,v 1.1 2018/04/06 10:17:58 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIWGET_INTERNAL_H_INCLUDED
+#define MINIWGET_INTERNAL_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+void * getHTTPResponse(SOCKET s, int * size, int * status_code);
+
+#endif
diff --git a/thirdparty/miniupnpc/minixml.c b/thirdparty/miniupnpc/minixml.c
new file mode 100644
index 00000000000..ed2d3c759c3
--- /dev/null
+++ b/thirdparty/miniupnpc/minixml.c
@@ -0,0 +1,231 @@
+/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * minixml.c : the minimum size a xml parser can be ! */
+/* Project : miniupnp
+ * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+
+Copyright (c) 2005-2017, Thomas BERNARD
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include "minixml.h"
+
+/* parseatt : used to parse the argument list
+ * return 0 (false) in case of success and -1 (true) if the end
+ * of the xmlbuffer is reached. */
+static int parseatt(struct xmlparser * p)
+{
+ const char * attname;
+ int attnamelen;
+ const char * attvalue;
+ int attvaluelen;
+ while(p->xml < p->xmlend)
+ {
+ if(*p->xml=='/' || *p->xml=='>')
+ return 0;
+ if( !IS_WHITE_SPACE(*p->xml) )
+ {
+ char sep;
+ attname = p->xml;
+ attnamelen = 0;
+ while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
+ {
+ attnamelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(*(p->xml++) != '=')
+ {
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ while(IS_WHITE_SPACE(*p->xml))
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ sep = *p->xml;
+ if(sep=='\'' || sep=='\"')
+ {
+ p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while(*p->xml != sep)
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ else
+ {
+ attvalue = p->xml;
+ attvaluelen = 0;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && *p->xml != '>' && *p->xml != '/')
+ {
+ attvaluelen++; p->xml++;
+ if(p->xml >= p->xmlend)
+ return -1;
+ }
+ }
+ /*printf("%.*s='%.*s'\n",
+ attnamelen, attname, attvaluelen, attvalue);*/
+ if(p->attfunc)
+ p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
+ }
+ p->xml++;
+ }
+ return -1;
+}
+
+/* parseelt parse the xml stream and
+ * call the callback functions when needed... */
+static void parseelt(struct xmlparser * p)
+{
+ int i;
+ const char * elementname;
+ while(p->xml < (p->xmlend - 1))
+ {
+ if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "", 3) != 0);
+ p->xml += 3;
+ }
+ else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
+ {
+ i = 0; elementname = ++p->xml;
+ while( !IS_WHITE_SPACE(*p->xml)
+ && (*p->xml!='>') && (*p->xml!='/')
+ )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ /* to ignore namespace : */
+ if(*p->xml==':')
+ {
+ i = 0;
+ elementname = ++p->xml;
+ }
+ }
+ if(i>0)
+ {
+ if(p->starteltfunc)
+ p->starteltfunc(p->data, elementname, i);
+ if(parseatt(p))
+ return;
+ if(*p->xml!='/')
+ {
+ const char * data;
+ i = 0; data = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while( IS_WHITE_SPACE(*p->xml) )
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ /* CDATA are at least 9 + 3 characters long : */
+ if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "xml += 9;
+ data = p->xml;
+ i = 0;
+ while(memcmp(p->xml, "]]>", 3) != 0)
+ {
+ i++; p->xml++;
+ if ((p->xml + 3) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc)
+ p->datafunc(p->data, data, i);
+ while(*p->xml!='<')
+ {
+ p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ }
+ else
+ {
+ while(*p->xml!='<')
+ {
+ i++; p->xml++;
+ if ((p->xml + 1) >= p->xmlend)
+ return;
+ }
+ if(i>0 && p->datafunc && *(p->xml + 1) == '/')
+ p->datafunc(p->data, data, i);
+ }
+ }
+ }
+ else if(*p->xml == '/')
+ {
+ i = 0; elementname = ++p->xml;
+ if (p->xml >= p->xmlend)
+ return;
+ while((*p->xml != '>'))
+ {
+ i++; p->xml++;
+ if (p->xml >= p->xmlend)
+ return;
+ }
+ if(p->endeltfunc)
+ p->endeltfunc(p->data, elementname, i);
+ p->xml++;
+ }
+ }
+ else
+ {
+ p->xml++;
+ }
+ }
+}
+
+/* the parser must be initialized before calling this function */
+void parsexml(struct xmlparser * parser)
+{
+ parser->xml = parser->xmlstart;
+ parser->xmlend = parser->xmlstart + parser->xmlsize;
+ parseelt(parser);
+}
+
+
diff --git a/thirdparty/miniupnpc/minixml.h b/thirdparty/miniupnpc/minixml.h
new file mode 100644
index 00000000000..19e6f513bf8
--- /dev/null
+++ b/thirdparty/miniupnpc/minixml.h
@@ -0,0 +1,37 @@
+/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
+/* minimal xml parser
+ *
+ * Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef MINIXML_H_INCLUDED
+#define MINIXML_H_INCLUDED
+#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
+
+/* if a callback function pointer is set to NULL,
+ * the function is not called */
+struct xmlparser {
+ const char *xmlstart;
+ const char *xmlend;
+ const char *xml; /* pointer to current character */
+ int xmlsize;
+ void * data;
+ void (*starteltfunc) (void *, const char *, int);
+ void (*endeltfunc) (void *, const char *, int);
+ void (*datafunc) (void *, const char *, int);
+ void (*attfunc) (void *, const char *, int, const char *, int);
+};
+
+/* parsexml()
+ * the xmlparser structure must be initialized before the call
+ * the following structure members have to be initialized :
+ * xmlstart, xmlsize, data, *func
+ * xml is for internal usage, xmlend is computed automatically */
+void parsexml(struct xmlparser *);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/minixmlvalid.c b/thirdparty/miniupnpc/minixmlvalid.c
new file mode 100644
index 00000000000..dad14881226
--- /dev/null
+++ b/thirdparty/miniupnpc/minixmlvalid.c
@@ -0,0 +1,163 @@
+/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
+/* MiniUPnP Project
+ * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
+ * minixmlvalid.c :
+ * validation program for the minixml parser
+ *
+ * (c) 2006-2011 Thomas Bernard */
+
+#include
+#include
+#include
+#include "minixml.h"
+
+/* xml event structure */
+struct event {
+ enum { ELTSTART, ELTEND, ATT, CHARDATA } type;
+ const char * data;
+ int len;
+};
+
+struct eventlist {
+ int n;
+ struct event * events;
+};
+
+/* compare 2 xml event lists
+ * return 0 if the two lists are equals */
+int evtlistcmp(struct eventlist * a, struct eventlist * b)
+{
+ int i;
+ struct event * ae, * be;
+ if(a->n != b->n)
+ {
+ printf("event number not matching : %d != %d\n", a->n, b->n);
+ /*return 1;*/
+ }
+ for(i=0; in; i++)
+ {
+ ae = a->events + i;
+ be = b->events + i;
+ if( (ae->type != be->type)
+ ||(ae->len != be->len)
+ ||memcmp(ae->data, be->data, ae->len))
+ {
+ printf("Found a difference : %d '%.*s' != %d '%.*s'\n",
+ ae->type, ae->len, ae->data,
+ be->type, be->len, be->data);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Test data */
+static const char xmldata[] =
+"\n"
+" "
+"character data"
+" \n \t"
+""
+"\nstuff !\n ]]> \n\n"
+" \tchardata1 chardata2 "
+"";
+
+static const struct event evtref[] =
+{
+ {ELTSTART, "xmlroot", 7},
+ {ELTSTART, "elt1", 4},
+ /* attributes */
+ {CHARDATA, "character data", 14},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt1b", 5},
+ {ELTSTART, "elt1", 4},
+ {CHARDATA, " stuff !\n ", 16},
+ {ELTEND, "elt1", 4},
+ {ELTSTART, "elt2a", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, "chardata1", 9},
+ {ELTEND, "elt2b", 5},
+ {ELTSTART, "elt2b", 5},
+ {CHARDATA, " chardata2 ", 11},
+ {ELTEND, "elt2b", 5},
+ {ELTEND, "elt2a", 5},
+ {ELTEND, "xmlroot", 7}
+};
+
+void startelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("startelt : %.*s\n", l, p);*/
+ evt->type = ELTSTART;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void endelt(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("endelt : %.*s\n", l, p);*/
+ evt->type = ELTEND;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+void chardata(void * data, const char * p, int l)
+{
+ struct eventlist * evtlist = data;
+ struct event * evt;
+ evt = evtlist->events + evtlist->n;
+ /*printf("chardata : '%.*s'\n", l, p);*/
+ evt->type = CHARDATA;
+ evt->data = p;
+ evt->len = l;
+ evtlist->n++;
+}
+
+int testxmlparser(const char * xml, int size)
+{
+ int r;
+ struct eventlist evtlist;
+ struct eventlist evtlistref;
+ struct xmlparser parser;
+ evtlist.n = 0;
+ evtlist.events = malloc(sizeof(struct event)*100);
+ if(evtlist.events == NULL)
+ {
+ fprintf(stderr, "Memory allocation error.\n");
+ return -1;
+ }
+ memset(&parser, 0, sizeof(parser));
+ parser.xmlstart = xml;
+ parser.xmlsize = size;
+ parser.data = &evtlist;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = chardata;
+ parsexml(&parser);
+ printf("%d events\n", evtlist.n);
+ /* compare */
+ evtlistref.n = sizeof(evtref)/sizeof(struct event);
+ evtlistref.events = (struct event *)evtref;
+ r = evtlistcmp(&evtlistref, &evtlist);
+ free(evtlist.events);
+ return r;
+}
+
+int main(int argc, char * * argv)
+{
+ int r;
+ (void)argc; (void)argv;
+
+ r = testxmlparser(xmldata, sizeof(xmldata)-1);
+ if(r)
+ printf("minixml validation test failed\n");
+ return r;
+}
+
diff --git a/thirdparty/miniupnpc/portlistingparse.c b/thirdparty/miniupnpc/portlistingparse.c
new file mode 100644
index 00000000000..55859f2714f
--- /dev/null
+++ b/thirdparty/miniupnpc/portlistingparse.c
@@ -0,0 +1,172 @@
+/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011-2016 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#include
+#include
+#ifdef DEBUG
+#include
+#endif /* DEBUG */
+#include "portlistingparse.h"
+#include "minixml.h"
+
+/* list of the elements */
+static const struct {
+ const portMappingElt code;
+ const char * const str;
+} elements[] = {
+ { PortMappingEntry, "PortMappingEntry"},
+ { NewRemoteHost, "NewRemoteHost"},
+ { NewExternalPort, "NewExternalPort"},
+ { NewProtocol, "NewProtocol"},
+ { NewInternalPort, "NewInternalPort"},
+ { NewInternalClient, "NewInternalClient"},
+ { NewEnabled, "NewEnabled"},
+ { NewDescription, "NewDescription"},
+ { NewLeaseTime, "NewLeaseTime"},
+ { PortMappingEltNone, NULL}
+};
+
+/* Helper function */
+static UNSIGNED_INTEGER
+atoui(const char * p, int l)
+{
+ UNSIGNED_INTEGER r = 0;
+ while(l > 0 && *p)
+ {
+ if(*p >= '0' && *p <= '9')
+ r = r*10 + (*p - '0');
+ else
+ break;
+ p++;
+ l--;
+ }
+ return r;
+}
+
+/* Start element handler */
+static void
+startelt(void * d, const char * name, int l)
+{
+ int i;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pdata->curelt = PortMappingEltNone;
+ for(i = 0; elements[i].str; i++)
+ {
+ if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
+ {
+ pdata->curelt = elements[i].code;
+ break;
+ }
+ }
+ if(pdata->curelt == PortMappingEntry)
+ {
+ struct PortMapping * pm;
+ pm = calloc(1, sizeof(struct PortMapping));
+ if(pm == NULL)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "startelt");
+#endif /* DEBUG */
+ return;
+ }
+ pm->l_next = pdata->l_head; /* insert in list */
+ pdata->l_head = pm;
+ }
+}
+
+/* End element handler */
+static void
+endelt(void * d, const char * name, int l)
+{
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ (void)name;
+ (void)l;
+ pdata->curelt = PortMappingEltNone;
+}
+
+/* Data handler */
+static void
+data(void * d, const char * data, int l)
+{
+ struct PortMapping * pm;
+ struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+ pm = pdata->l_head;
+ if(!pm)
+ return;
+ if(l > 63)
+ l = 63;
+ switch(pdata->curelt)
+ {
+ case NewRemoteHost:
+ memcpy(pm->remoteHost, data, l);
+ pm->remoteHost[l] = '\0';
+ break;
+ case NewExternalPort:
+ pm->externalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewProtocol:
+ if(l > 3)
+ l = 3;
+ memcpy(pm->protocol, data, l);
+ pm->protocol[l] = '\0';
+ break;
+ case NewInternalPort:
+ pm->internalPort = (unsigned short)atoui(data, l);
+ break;
+ case NewInternalClient:
+ memcpy(pm->internalClient, data, l);
+ pm->internalClient[l] = '\0';
+ break;
+ case NewEnabled:
+ pm->enabled = (unsigned char)atoui(data, l);
+ break;
+ case NewDescription:
+ memcpy(pm->description, data, l);
+ pm->description[l] = '\0';
+ break;
+ case NewLeaseTime:
+ pm->leaseTime = atoui(data, l);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* Parse the PortMappingList XML document for IGD version 2
+ */
+void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata)
+{
+ struct xmlparser parser;
+
+ memset(pdata, 0, sizeof(struct PortMappingParserData));
+ /* init xmlparser */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = pdata;
+ parser.starteltfunc = startelt;
+ parser.endeltfunc = endelt;
+ parser.datafunc = data;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+FreePortListing(struct PortMappingParserData * pdata)
+{
+ struct PortMapping * pm;
+ while((pm = pdata->l_head) != NULL)
+ {
+ /* remove from list */
+ pdata->l_head = pm->l_next;
+ free(pm);
+ }
+}
+
diff --git a/thirdparty/miniupnpc/portlistingparse.h b/thirdparty/miniupnpc/portlistingparse.h
new file mode 100644
index 00000000000..e3957a3f4c1
--- /dev/null
+++ b/thirdparty/miniupnpc/portlistingparse.h
@@ -0,0 +1,65 @@
+/* $Id: portlistingparse.h,v 1.10 2014/11/01 10:37:32 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011-2015 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#ifndef PORTLISTINGPARSE_H_INCLUDED
+#define PORTLISTINGPARSE_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+/* for the definition of UNSIGNED_INTEGER */
+#include "miniupnpctypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* sample of PortMappingEntry :
+
+ 202.233.2.1
+ 2345
+ TCP
+ 2345
+ 192.168.1.137
+ 1
+ dooom
+ 345
+
+ */
+typedef enum { PortMappingEltNone,
+ PortMappingEntry, NewRemoteHost,
+ NewExternalPort, NewProtocol,
+ NewInternalPort, NewInternalClient,
+ NewEnabled, NewDescription,
+ NewLeaseTime } portMappingElt;
+
+struct PortMapping {
+ struct PortMapping * l_next; /* list next element */
+ UNSIGNED_INTEGER leaseTime;
+ unsigned short externalPort;
+ unsigned short internalPort;
+ char remoteHost[64];
+ char internalClient[64];
+ char description[64];
+ char protocol[4];
+ unsigned char enabled;
+};
+
+struct PortMappingParserData {
+ struct PortMapping * l_head; /* list head */
+ portMappingElt curelt;
+};
+
+MINIUPNP_LIBSPEC void
+ParsePortListing(const char * buffer, int bufsize,
+ struct PortMappingParserData * pdata);
+
+MINIUPNP_LIBSPEC void
+FreePortListing(struct PortMappingParserData * pdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/receivedata.c b/thirdparty/miniupnpc/receivedata.c
new file mode 100644
index 00000000000..7b9cc5b7786
--- /dev/null
+++ b/thirdparty/miniupnpc/receivedata.c
@@ -0,0 +1,99 @@
+/* $Id: receivedata.c,v 1.7 2015/11/09 21:51:41 nanard Exp $ */
+/* Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2011-2014 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include
+#include
+#ifdef _WIN32
+#include
+#include
+#else /* _WIN32 */
+#include
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include
+#include
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include
+#endif /* !defined(__amigaos__) && !defined(__amigaos4__) */
+#include
+#define MINIUPNPC_IGNORE_EINTR
+#endif /* _WIN32 */
+
+#include "receivedata.h"
+
+int
+receivedata(SOCKET socket,
+ char * data, int length,
+ int timeout, unsigned int * scope_id)
+{
+#ifdef MINIUPNPC_GET_SRC_ADDR
+ struct sockaddr_storage src_addr;
+ socklen_t src_addr_len = sizeof(src_addr);
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ int n;
+#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+ /* using poll */
+ struct pollfd fds[1]; /* for the poll */
+#ifdef MINIUPNPC_IGNORE_EINTR
+ do {
+#endif /* MINIUPNPC_IGNORE_EINTR */
+ fds[0].fd = socket;
+ fds[0].events = POLLIN;
+ n = poll(fds, 1, timeout);
+#ifdef MINIUPNPC_IGNORE_EINTR
+ } while(n < 0 && errno == EINTR);
+#endif /* MINIUPNPC_IGNORE_EINTR */
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("poll");
+ return -1;
+ } else if(n == 0) {
+ /* timeout */
+ return 0;
+ }
+#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+ /* using select under _WIN32 and amigaos */
+ fd_set socketSet;
+ TIMEVAL timeval;
+ FD_ZERO(&socketSet);
+ FD_SET(socket, &socketSet);
+ timeval.tv_sec = timeout / 1000;
+ timeval.tv_usec = (timeout % 1000) * 1000;
+ n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
+ if(n < 0) {
+ PRINT_SOCKET_ERROR("select");
+ return -1;
+ } else if(n == 0) {
+ return 0;
+ }
+#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+#ifdef MINIUPNPC_GET_SRC_ADDR
+ memset(&src_addr, 0, sizeof(src_addr));
+ n = recvfrom(socket, data, length, 0,
+ (struct sockaddr *)&src_addr, &src_addr_len);
+#else /* MINIUPNPC_GET_SRC_ADDR */
+ n = recv(socket, data, length, 0);
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ if(n<0) {
+ PRINT_SOCKET_ERROR("recv");
+ }
+#ifdef MINIUPNPC_GET_SRC_ADDR
+ if (src_addr.ss_family == AF_INET6) {
+ const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
+#ifdef DEBUG
+ printf("scope_id=%u\n", src_addr6->sin6_scope_id);
+#endif /* DEBUG */
+ if(scope_id)
+ *scope_id = src_addr6->sin6_scope_id;
+ }
+#endif /* MINIUPNPC_GET_SRC_ADDR */
+ return n;
+}
+
diff --git a/thirdparty/miniupnpc/receivedata.h b/thirdparty/miniupnpc/receivedata.h
new file mode 100644
index 00000000000..c9fdc561f8d
--- /dev/null
+++ b/thirdparty/miniupnpc/receivedata.h
@@ -0,0 +1,21 @@
+/* $Id: receivedata.h,v 1.3 2012/06/23 22:34:47 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2011-2018 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef RECEIVEDATA_H_INCLUDED
+#define RECEIVEDATA_H_INCLUDED
+
+#include "miniupnpc_socketdef.h"
+
+/* Reads data from the specified socket.
+ * Returns the number of bytes read if successful, zero if no bytes were
+ * read or if we timed out. Returns negative if there was an error. */
+int receivedata(SOCKET socket,
+ char * data, int length,
+ int timeout, unsigned int * scope_id);
+
+#endif
+
diff --git a/thirdparty/miniupnpc/upnpc.c b/thirdparty/miniupnpc/upnpc.c
new file mode 100644
index 00000000000..0c65cbe8c0e
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpc.c
@@ -0,0 +1,861 @@
+/* $Id: upnpc.c,v 1.119 2018/03/13 23:34:46 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#define snprintf _snprintf
+#else
+/* for IPPROTO_TCP / IPPROTO_UDP */
+#include
+#endif
+#include
+#include "miniwget.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "portlistingparse.h"
+#include "upnperrors.h"
+#include "miniupnpcstrings.h"
+
+/* protofix() checks if protocol is "UDP" or "TCP"
+ * returns NULL if not */
+const char * protofix(const char * proto)
+{
+ static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
+ static const char proto_udp[4] = { 'U', 'D', 'P', 0};
+ int i, b;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_tcp[i])
+ || (proto[i] == (proto_tcp[i] | 32)) );
+ if(b)
+ return proto_tcp;
+ for(i=0, b=1; i<4; i++)
+ b = b && ( (proto[i] == proto_udp[i])
+ || (proto[i] == (proto_udp[i] | 32)) );
+ if(b)
+ return proto_udp;
+ return 0;
+}
+
+/* is_int() checks if parameter is an integer or not
+ * 1 for integer
+ * 0 for not an integer */
+int is_int(char const* s)
+{
+ if(s == NULL)
+ return 0;
+ while(*s) {
+ /* #define isdigit(c) ((c) >= '0' && (c) <= '9') */
+ if(!isdigit(*s))
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
+static void DisplayInfos(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ char externalIPAddress[40];
+ char connectionType[64];
+ char status[64];
+ char lastconnerr[64];
+ unsigned int uptime = 0;
+ unsigned int brUp, brDown;
+ time_t timenow, timestarted;
+ int r;
+ if(UPNP_GetConnectionTypeInfo(urls->controlURL,
+ data->first.servicetype,
+ connectionType) != UPNPCOMMAND_SUCCESS)
+ printf("GetConnectionTypeInfo failed.\n");
+ else
+ printf("Connection Type : %s\n", connectionType);
+ if(UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+ status, &uptime, lastconnerr) != UPNPCOMMAND_SUCCESS)
+ printf("GetStatusInfo failed.\n");
+ else
+ printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
+ status, uptime, lastconnerr);
+ if(uptime > 0) {
+ timenow = time(NULL);
+ timestarted = timenow - uptime;
+ printf(" Time started : %s", ctime(×tarted));
+ }
+ if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
+ &brDown, &brUp) != UPNPCOMMAND_SUCCESS) {
+ printf("GetLinkLayerMaxBitRates failed.\n");
+ } else {
+ printf("MaxBitRateDown : %u bps", brDown);
+ if(brDown >= 1000000) {
+ printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10);
+ } else if(brDown >= 1000) {
+ printf(" (%u Kbps)", brDown / 1000);
+ }
+ printf(" MaxBitRateUp %u bps", brUp);
+ if(brUp >= 1000000) {
+ printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10);
+ } else if(brUp >= 1000) {
+ printf(" (%u Kbps)", brUp / 1000);
+ }
+ printf("\n");
+ }
+ r = UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(r != UPNPCOMMAND_SUCCESS) {
+ printf("GetExternalIPAddress failed. (errorcode=%d)\n", r);
+ } else {
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+ }
+}
+
+static void GetConnectionStatus(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ DisplayInfos(urls, data);
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+static void ListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ char index[6];
+ char intClient[40];
+ char intPort[6];
+ char extPort[6];
+ char protocol[4];
+ char desc[80];
+ char enabled[6];
+ char rHost[64];
+ char duration[16];
+ /*unsigned int num=0;
+ UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
+ printf("PortMappingNumberOfEntries : %u\n", num);*/
+ printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
+ do {
+ snprintf(index, 6, "%d", i);
+ rHost[0] = '\0'; enabled[0] = '\0';
+ duration[0] = '\0'; desc[0] = '\0';
+ extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+ r = UPNP_GetGenericPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ index,
+ extPort, intClient, intPort,
+ protocol, desc, enabled,
+ rHost, duration);
+ if(r==0)
+ /*
+ printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
+ " desc='%s' rHost='%s'\n",
+ i, protocol, extPort, intClient, intPort,
+ enabled, duration,
+ desc, rHost);
+ */
+ printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n",
+ i, protocol, extPort, intClient, intPort,
+ desc, rHost, duration);
+ else
+ printf("GetGenericPortMappingEntry() returned %d (%s)\n",
+ r, strupnperror(r));
+ i++;
+ } while(r==0);
+}
+
+static void NewListRedirections(struct UPNPUrls * urls,
+ struct IGDdatas * data)
+{
+ int r;
+ int i = 0;
+ struct PortMappingParserData pdata;
+ struct PortMapping * pm;
+
+ memset(&pdata, 0, sizeof(struct PortMappingParserData));
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "TCP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
+ for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+ r = UPNP_GetListOfPortMappings(urls->controlURL,
+ data->first.servicetype,
+ "0",
+ "65535",
+ "UDP",
+ "1000",
+ &pdata);
+ if(r == UPNPCOMMAND_SUCCESS)
+ {
+ for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost,
+ (unsigned)pm->leaseTime);
+ i++;
+ }
+ FreePortListing(&pdata);
+ }
+ else
+ {
+ printf("GetListOfPortMappings() returned %d (%s)\n",
+ r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * 1 - get connection type
+ * 2 - get extenal ip address
+ * 3 - Add port mapping
+ * 4 - get this port mapping from the IGD */
+static int SetRedirectAndTest(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * iaddr,
+ const char * iport,
+ const char * eport,
+ const char * proto,
+ const char * leaseDuration,
+ const char * description,
+ int addAny)
+{
+ char externalIPAddress[40];
+ char intClient[40];
+ char intPort[6];
+ char reservedPort[6];
+ char duration[16];
+ int r;
+
+ if(!iaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return -1;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return -1;
+ }
+
+ r = UPNP_GetExternalIPAddress(urls->controlURL,
+ data->first.servicetype,
+ externalIPAddress);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetExternalIPAddress failed.\n");
+ else
+ printf("ExternalIPAddress = %s\n", externalIPAddress);
+
+ if (addAny) {
+ r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype,
+ eport, iport, iaddr, description,
+ proto, 0, leaseDuration, reservedPort);
+ if(r==UPNPCOMMAND_SUCCESS)
+ eport = reservedPort;
+ else
+ printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ eport, iport, iaddr, r, strupnperror(r));
+ } else {
+ r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
+ eport, iport, iaddr, description,
+ proto, 0, leaseDuration);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+ eport, iport, iaddr, r, strupnperror(r));
+ return -2;
+ }
+ }
+
+ r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
+ data->first.servicetype,
+ eport, proto, NULL/*remoteHost*/,
+ intClient, intPort, NULL/*desc*/,
+ NULL/*enabled*/, duration);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
+ r, strupnperror(r));
+ return -2;
+ } else {
+ printf("InternalIP:Port = %s:%s\n", intClient, intPort);
+ printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
+ externalIPAddress, eport, proto, intClient, intPort, duration);
+ }
+ return 0;
+}
+
+static int
+RemoveRedirect(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * eport,
+ const char * proto,
+ const char * remoteHost)
+{
+ int r;
+ if(!proto || !eport)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return -1;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "protocol invalid\n");
+ return -1;
+ }
+ r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMapping() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMapping() returned : %d\n", r);
+ }
+ return 0;
+}
+
+static int
+RemoveRedirectRange(struct UPNPUrls * urls,
+ struct IGDdatas * data,
+ const char * ePortStart, char const * ePortEnd,
+ const char * proto, const char * manage)
+{
+ int r;
+
+ if (!manage)
+ manage = "0";
+
+ if(!proto || !ePortStart || !ePortEnd)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return -1;
+ }
+ proto = protofix(proto);
+ if(!proto)
+ {
+ fprintf(stderr, "protocol invalid\n");
+ return -1;
+ }
+ r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
+ }
+ return 0;
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+ unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+ int firewallEnabled = 0, inboundPinholeAllowed = 0;
+
+ UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed);
+ printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed);
+ printf("GetFirewallStatus:\n Firewall Enabled: %s\n Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No");
+
+ bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+ bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+ packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+ printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+ printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+/* Test function
+ * 1 - Add pinhole
+ * 2 - Check if pinhole is working from the IGD side */
+static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto, const char * lease_time)
+{
+ char uniqueID[8];
+ /*int isWorking = 0;*/
+ int r;
+ char proto_tmp[8];
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ if(atoi(proto) == 0)
+ {
+ const char * protocol;
+ protocol = protofix(proto);
+ if(protocol && (strcmp("TCP", protocol) == 0))
+ {
+ snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_TCP);
+ proto = proto_tmp;
+ }
+ else if(protocol && (strcmp("UDP", protocol) == 0))
+ {
+ snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_UDP);
+ proto = proto_tmp;
+ }
+ else
+ {
+ fprintf(stderr, "invalid protocol\n");
+ return;
+ }
+ }
+ r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ remoteaddr, eport, intaddr, iport, r, strupnperror(r));
+ else
+ {
+ printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n",
+ remoteaddr, eport, intaddr, iport, uniqueID);
+ /*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/
+ }
+}
+
+/* Test function
+ * 1 - Check if pinhole is working from the IGD side
+ * 2 - Update pinhole */
+static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * uniqueID, const char * lease_time)
+{
+ int isWorking = 0;
+ int r;
+
+ if(!uniqueID || !lease_time)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ if(isWorking || r==709)
+ {
+ r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time);
+ printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r));
+ }
+}
+
+/* Test function
+ * Get pinhole timeout
+ */
+static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data,
+ const char * remoteaddr, const char * eport,
+ const char * intaddr, const char * iport,
+ const char * proto)
+{
+ int timeout = 0;
+ int r;
+
+ if(!intaddr || !remoteaddr || !iport || !eport || !proto)
+ {
+ fprintf(stderr, "Wrong arguments\n");
+ return;
+ }
+
+ r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+ intaddr, iport, remoteaddr, eport, r, strupnperror(r));
+ else
+ printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout);
+}
+
+static void
+GetPinholePackets(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, pinholePackets = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets);
+}
+
+static void
+CheckPinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r, isWorking = 0;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+ if(r!=UPNPCOMMAND_SUCCESS)
+ printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+ else
+ printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+}
+
+static void
+RemovePinhole(struct UPNPUrls * urls,
+ struct IGDdatas * data, const char * uniqueID)
+{
+ int r;
+ if(!uniqueID)
+ {
+ fprintf(stderr, "invalid arguments\n");
+ return;
+ }
+ r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID);
+ printf("UPNP_DeletePinhole() returned : %d\n", r);
+}
+
+
+/* sample upnp client program */
+int main(int argc, char ** argv)
+{
+ char command = 0;
+ char ** commandargv = 0;
+ int commandargc = 0;
+ struct UPNPDev * devlist = 0;
+ char lanaddr[64] = "unset"; /* my ip address on the LAN */
+ int i;
+ const char * rootdescurl = 0;
+ const char * multicastif = 0;
+ const char * minissdpdpath = 0;
+ int localport = UPNP_LOCAL_PORT_ANY;
+ int retcode = 0;
+ int error = 0;
+ int ipv6 = 0;
+ unsigned char ttl = 2; /* defaulting to 2 */
+ const char * description = 0;
+
+#ifdef _WIN32
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+ if(nResult != NO_ERROR)
+ {
+ fprintf(stderr, "WSAStartup() failed.\n");
+ return -1;
+ }
+#endif
+ printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
+ printf(" (c) 2005-2018 Thomas Bernard.\n");
+ printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n"
+ "for more information.\n");
+ /* command line processing */
+ for(i=1; i65535 ||
+ (localport >1 && localport < 1024))
+ {
+ fprintf(stderr, "Invalid localport '%s'\n", argv[i]);
+ localport = UPNP_LOCAL_PORT_ANY;
+ break;
+ }
+ }
+ else if(argv[i][1] == 'p')
+ minissdpdpath = argv[++i];
+ else if(argv[i][1] == '6')
+ ipv6 = 1;
+ else if(argv[i][1] == 'e')
+ description = argv[++i];
+ else if(argv[i][1] == 't')
+ ttl = (unsigned char)atoi(argv[++i]);
+ else
+ {
+ command = argv[i][1];
+ i++;
+ commandargv = argv + i;
+ commandargc = argc - i;
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "option '%s' invalid\n", argv[i]);
+ }
+ }
+
+ if(!command
+ || (command == 'a' && commandargc<4)
+ || (command == 'd' && argc<2)
+ || (command == 'r' && argc<2)
+ || (command == 'A' && commandargc<6)
+ || (command == 'U' && commandargc<2)
+ || (command == 'D' && commandargc<1))
+ {
+ fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration]\n\t\tAdd port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -d external_port protocol \n\t\tDelete port redirection\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -l\n\t\tList redirections\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -n ip port external_port protocol [duration]\n\t\tAdd (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -N external_port_start external_port_end protocol [manage]\n\t\tDelete range of port redirections (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -K uniqueID\n\t\tGet Number of packets going through the rule (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -D uniqueID\n\t\tDelete Pinhole (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -S\n\t\tGet Firewall status (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]);
+ fprintf(stderr, " \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]);
+ fprintf(stderr, "\nprotocol is UDP or TCP\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -e description : set description for port mapping.\n");
+ fprintf(stderr, " -6 : use ip v6 instead of ip v4.\n");
+ fprintf(stderr, " -u url : bypass discovery process by providing the XML root description url.\n");
+ fprintf(stderr, " -m address/interface : provide ip address (ip v4) or interface name (ip v4 or v6) to use for sending SSDP multicast packets.\n");
+ fprintf(stderr, " -z localport : SSDP packets local (source) port (1024-65535).\n");
+ fprintf(stderr, " -p path : use this path for MiniSSDPd socket.\n");
+ fprintf(stderr, " -t ttl : set multicast TTL. Default value is 2.\n");
+ return 1;
+ }
+
+ if( rootdescurl
+ || (devlist = upnpDiscover(2000, multicastif, minissdpdpath,
+ localport, ipv6, ttl, &error)))
+ {
+ struct UPNPDev * device;
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ if(devlist)
+ {
+ printf("List of UPNP devices found on the network :\n");
+ for(device = devlist; device; device = device->pNext)
+ {
+ printf(" desc: %s\n st: %s\n\n",
+ device->descURL, device->st);
+ }
+ }
+ else if(!rootdescurl)
+ {
+ printf("upnpDiscover() error code=%d\n", error);
+ }
+ i = 1;
+ if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr)))
+ || (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))))
+ {
+ switch(i) {
+ case 1:
+ printf("Found valid IGD : %s\n", urls.controlURL);
+ break;
+ case 2:
+ printf("Found a (not connected?) IGD : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ case 3:
+ printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ break;
+ default:
+ printf("Found device (igd ?) : %s\n", urls.controlURL);
+ printf("Trying to continue anyway\n");
+ }
+ printf("Local LAN ip address : %s\n", lanaddr);
+ #if 0
+ printf("getting \"%s\"\n", urls.ipcondescURL);
+ descXML = miniwget(urls.ipcondescURL, &descXMLsize);
+ if(descXML)
+ {
+ /*fwrite(descXML, 1, descXMLsize, stdout);*/
+ free(descXML); descXML = NULL;
+ }
+ #endif
+
+ switch(command)
+ {
+ case 'l':
+ DisplayInfos(&urls, &data);
+ ListRedirections(&urls, &data);
+ break;
+ case 'L':
+ NewListRedirections(&urls, &data);
+ break;
+ case 'a':
+ if (SetRedirectAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ (commandargc > 4)?commandargv[4]:"0",
+ description, 0) < 0)
+ retcode = 2;
+ break;
+ case 'd':
+ if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
+ commandargc > 2 ? commandargv[2] : NULL) < 0)
+ retcode = 2;
+ break;
+ case 'n': /* aNy */
+ if (SetRedirectAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ (commandargc > 4)?commandargv[4]:"0",
+ description, 1) < 0)
+ retcode = 2;
+ break;
+ case 'N':
+ if (commandargc < 3)
+ fprintf(stderr, "too few arguments\n");
+
+ if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
+ commandargc > 3 ? commandargv[3] : NULL) < 0)
+ retcode = 2;
+ break;
+ case 's':
+ GetConnectionStatus(&urls, &data);
+ break;
+ case 'r':
+ i = 0;
+ while(i */
+ if (SetRedirectAndTest(&urls, &data,
+ lanaddr, commandargv[i],
+ commandargv[i+1], commandargv[i+2], "0",
+ description, 0) < 0)
+ retcode = 2;
+ i+=3; /* 3 parameters parsed */
+ } else {
+ /* 2nd parameter not an integer : */
+ if (SetRedirectAndTest(&urls, &data,
+ lanaddr, commandargv[i],
+ commandargv[i], commandargv[i+1], "0",
+ description, 0) < 0)
+ retcode = 2;
+ i+=2; /* 2 parameters parsed */
+ }
+ }
+ break;
+ case 'A':
+ SetPinholeAndTest(&urls, &data,
+ commandargv[0], commandargv[1],
+ commandargv[2], commandargv[3],
+ commandargv[4], commandargv[5]);
+ break;
+ case 'U':
+ GetPinholeAndUpdate(&urls, &data,
+ commandargv[0], commandargv[1]);
+ break;
+ case 'C':
+ for(i=0; i
+#include
+#include
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+#include "portlistingparse.h"
+#include "upnpreplyparse.h"
+
+static UNSIGNED_INTEGER
+my_atoui(const char * s)
+{
+ return s ? ((UNSIGNED_INTEGER)STRTOUI(s, NULL, 0)) : 0;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalBytesReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsSent", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/*
+ * */
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ unsigned int r = 0;
+ char * p;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetTotalPacketsReceived", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
+ r = my_atoui(p);
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+/* UPNP_GetStatusInfo() call the corresponding UPNP method
+ * returns the current status and uptime */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ char * up;
+ char * err;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!status && !uptime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetStatusInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ /*DisplayNameValueList(buffer, bufsize);*/
+ free(buffer); buffer = NULL;
+ up = GetValueFromNameValueList(&pdata, "NewUptime");
+ p = GetValueFromNameValueList(&pdata, "NewConnectionStatus");
+ err = GetValueFromNameValueList(&pdata, "NewLastConnectionError");
+ if(p && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(status) {
+ if(p){
+ strncpy(status, p, 64 );
+ status[63] = '\0';
+ }else
+ status[0]= '\0';
+ }
+
+ if(uptime) {
+ if(up)
+ sscanf(up,"%u",uptime);
+ else
+ *uptime = 0;
+ }
+
+ if(lastconnerror) {
+ if(err) {
+ strncpy(lastconnerror, err, 64 );
+ lastconnerror[63] = '\0';
+ } else
+ lastconnerror[0] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
+ * returns the connection type */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!connectionType)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetConnectionTypeInfo", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "NewConnectionType");
+ /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
+ /* PossibleConnectionTypes will have several values.... */
+ if(p) {
+ strncpy(connectionType, p, 64 );
+ connectionType[63] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ connectionType[0] = '\0';
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
+ * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
+ * One of the values can be null
+ * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only
+ * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
+ const char * servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ char * down;
+ char * up;
+ char * p;
+
+ if(!bitrateDown && !bitrateUp)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ /* shouldn't we use GetCommonLinkProperties ? */
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetCommonLinkProperties", 0, &bufsize))) {
+ /*"GetLinkLayerMaxBitRates", 0, &bufsize);*/
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
+ /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/
+ down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate");
+ up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate");
+ /*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/
+ /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkStatus");*/
+ if(down && up)
+ ret = UPNPCOMMAND_SUCCESS;
+
+ if(bitrateDown) {
+ if(down)
+ sscanf(down,"%u",bitrateDown);
+ else
+ *bitrateDown = 0;
+ }
+
+ if(bitrateUp) {
+ if(up)
+ sscanf(up,"%u",bitrateUp);
+ else
+ *bitrateUp = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!extIpAdd || !controlURL || !servicetype)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetExternalIPAddress", 0, &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
+ p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress");
+ if(p) {
+ strncpy(extIpAdd, p, 16 );
+ extIpAdd[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ extIpAdd[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration)
+{
+ struct UPNParg * AddPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!inPort || !inClient || !proto || !extPort)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+ if(AddPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ AddPortMappingArgs[0].elt = "NewRemoteHost";
+ AddPortMappingArgs[0].val = remoteHost;
+ AddPortMappingArgs[1].elt = "NewExternalPort";
+ AddPortMappingArgs[1].val = extPort;
+ AddPortMappingArgs[2].elt = "NewProtocol";
+ AddPortMappingArgs[2].val = proto;
+ AddPortMappingArgs[3].elt = "NewInternalPort";
+ AddPortMappingArgs[3].val = inPort;
+ AddPortMappingArgs[4].elt = "NewInternalClient";
+ AddPortMappingArgs[4].val = inClient;
+ AddPortMappingArgs[5].elt = "NewEnabled";
+ AddPortMappingArgs[5].val = "1";
+ AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+ AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+ AddPortMappingArgs[7].elt = "NewLeaseDuration";
+ AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ /*buffer[bufsize] = '\0';*/
+ /*puts(buffer);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort)
+{
+ struct UPNParg * AddPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!inPort || !inClient || !proto || !extPort)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+ if(AddPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ AddPortMappingArgs[0].elt = "NewRemoteHost";
+ AddPortMappingArgs[0].val = remoteHost;
+ AddPortMappingArgs[1].elt = "NewExternalPort";
+ AddPortMappingArgs[1].val = extPort;
+ AddPortMappingArgs[2].elt = "NewProtocol";
+ AddPortMappingArgs[2].val = proto;
+ AddPortMappingArgs[3].elt = "NewInternalPort";
+ AddPortMappingArgs[3].val = inPort;
+ AddPortMappingArgs[4].elt = "NewInternalClient";
+ AddPortMappingArgs[4].val = inClient;
+ AddPortMappingArgs[5].elt = "NewEnabled";
+ AddPortMappingArgs[5].val = "1";
+ AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+ AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+ AddPortMappingArgs[7].elt = "NewLeaseDuration";
+ AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddAnyPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ char *p;
+
+ p = GetValueFromNameValueList(&pdata, "NewReservedPort");
+ if(p) {
+ strncpy(reservedPort, p, 6);
+ reservedPort[5] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else {
+ ret = UPNPCOMMAND_INVALID_RESPONSE;
+ }
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ if(DeletePortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePortMappingArgs[0].elt = "NewRemoteHost";
+ DeletePortMappingArgs[0].val = remoteHost;
+ DeletePortMappingArgs[1].elt = "NewExternalPort";
+ DeletePortMappingArgs[1].val = extPort;
+ DeletePortMappingArgs[2].elt = "NewProtocol";
+ DeletePortMappingArgs[2].val = proto;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMapping",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage)
+{
+ struct UPNParg * DeletePortMappingArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!extPortStart || !extPortEnd || !proto || !manage)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePortMappingArgs = calloc(5, sizeof(struct UPNParg));
+ if(DeletePortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePortMappingArgs[0].elt = "NewStartPort";
+ DeletePortMappingArgs[0].val = extPortStart;
+ DeletePortMappingArgs[1].elt = "NewEndPort";
+ DeletePortMappingArgs[1].val = extPortEnd;
+ DeletePortMappingArgs[2].elt = "NewProtocol";
+ DeletePortMappingArgs[2].val = proto;
+ DeletePortMappingArgs[3].elt = "NewManage";
+ DeletePortMappingArgs[3].val = manage;
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMappingRange",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ } else {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int r = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!index)
+ return UPNPCOMMAND_INVALID_ARGS;
+ intClient[0] = '\0';
+ intPort[0] = '\0';
+ GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
+ if(GetPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPortMappingArgs[0].elt = "NewPortMappingIndex";
+ GetPortMappingArgs[0].val = index;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetGenericPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
+ if(p && rHost)
+ {
+ strncpy(rHost, p, 64);
+ rHost[63] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewExternalPort");
+ if(p && extPort)
+ {
+ strncpy(extPort, p, 6);
+ extPort[5] = '\0';
+ r = UPNPCOMMAND_SUCCESS;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewProtocol");
+ if(p && protocol)
+ {
+ strncpy(protocol, p, 4);
+ protocol[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p)
+ {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ r = 0;
+ }
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p)
+ {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled)
+ {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc)
+ {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && duration)
+ {
+ strncpy(duration, p, 16);
+ duration[15] = '\0';
+ }
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ r = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &r);
+ }
+ ClearNameValueList(&pdata);
+ return r;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
+ const char * servicetype,
+ unsigned int * numEntries)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char* p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPortMappingNumberOfEntries", 0,
+ &bufsize))) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+#ifdef DEBUG
+ DisplayNameValueList(buffer, bufsize);
+#endif
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries");
+ if(numEntries && p) {
+ *numEntries = 0;
+ sscanf(p, "%u", numEntries);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
+ * the result is returned in the intClient and intPort strings
+ * please provide 16 and 6 bytes of data */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPortMappingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!intPort || !intClient || !extPort || !proto)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPortMappingArgs = calloc(4, sizeof(struct UPNParg));
+ if(GetPortMappingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPortMappingArgs[0].elt = "NewRemoteHost";
+ GetPortMappingArgs[0].val = remoteHost;
+ GetPortMappingArgs[1].elt = "NewExternalPort";
+ GetPortMappingArgs[1].val = extPort;
+ GetPortMappingArgs[2].elt = "NewProtocol";
+ GetPortMappingArgs[2].val = proto;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetSpecificPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+ if(p) {
+ strncpy(intClient, p, 16);
+ intClient[15] = '\0';
+ ret = UPNPCOMMAND_SUCCESS;
+ } else
+ intClient[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+ if(p) {
+ strncpy(intPort, p, 6);
+ intPort[5] = '\0';
+ } else
+ intPort[0] = '\0';
+
+ p = GetValueFromNameValueList(&pdata, "NewEnabled");
+ if(p && enabled) {
+ strncpy(enabled, p, 4);
+ enabled[3] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+ if(p && desc) {
+ strncpy(desc, p, 80);
+ desc[79] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+ if(p && leaseDuration)
+ {
+ strncpy(leaseDuration, p, 16);
+ leaseDuration[15] = '\0';
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+/* UPNP_GetListOfPortMappings()
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetListOfPortMappingsArgs;
+ const char * p;
+ char * buffer;
+ int bufsize;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!startPort || !endPort || !protocol)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg));
+ if(GetListOfPortMappingsArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetListOfPortMappingsArgs[0].elt = "NewStartPort";
+ GetListOfPortMappingsArgs[0].val = startPort;
+ GetListOfPortMappingsArgs[1].elt = "NewEndPort";
+ GetListOfPortMappingsArgs[1].val = endPort;
+ GetListOfPortMappingsArgs[2].elt = "NewProtocol";
+ GetListOfPortMappingsArgs[2].val = protocol;
+ GetListOfPortMappingsArgs[3].elt = "NewManage";
+ GetListOfPortMappingsArgs[3].val = "1";
+ GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
+ GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetListOfPortMappings",
+ GetListOfPortMappingsArgs, &bufsize);
+ free(GetListOfPortMappingsArgs);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ /*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/
+ /*if(p) {
+ printf("NewPortListing : %s\n", p);
+ }*/
+ /*printf("NewPortListing(%d chars) : %s\n",
+ pdata.portListingLength, pdata.portListing);*/
+ if(pdata.portListing)
+ {
+ /*struct PortMapping * pm;
+ int i = 0;*/
+ ParsePortListing(pdata.portListing, pdata.portListingLength,
+ data);
+ ret = UPNPCOMMAND_SUCCESS;
+ /*
+ for(pm = data->head.lh_first; pm != NULL; pm = pm->entries.le_next)
+ {
+ printf("%2d %s %5hu->%s:%-5hu '%s' '%s'\n",
+ i, pm->protocol, pm->externalPort, pm->internalClient,
+ pm->internalPort,
+ pm->description, pm->remoteHost);
+ i++;
+ }
+ */
+ /*FreePortListing(&data);*/
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p) {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+
+ /*printf("%.*s", bufsize, buffer);*/
+
+ return ret;
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed)
+{
+ struct NameValueParserData pdata;
+ char * buffer;
+ int bufsize;
+ char * fe, *ipa, *p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!firewallEnabled || !inboundPinholeAllowed)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetFirewallStatus", 0, &bufsize);
+ if(!buffer) {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ fe = GetValueFromNameValueList(&pdata, "FirewallEnabled");
+ ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed");
+ if(ipa && fe)
+ ret = UPNPCOMMAND_SUCCESS;
+ if(fe)
+ *firewallEnabled = my_atoui(fe);
+ /*else
+ *firewallEnabled = 0;*/
+ if(ipa)
+ *inboundPinholeAllowed = my_atoui(ipa);
+ /*else
+ *inboundPinholeAllowed = 0;*/
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout)
+{
+ struct UPNParg * GetOutboundPinholeTimeoutArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remotePort || !remoteHost)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg));
+ if(GetOutboundPinholeTimeoutArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost";
+ GetOutboundPinholeTimeoutArgs[0].val = remoteHost;
+ GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort";
+ GetOutboundPinholeTimeoutArgs[1].val = remotePort;
+ GetOutboundPinholeTimeoutArgs[2].elt = "Protocol";
+ GetOutboundPinholeTimeoutArgs[2].val = proto;
+ GetOutboundPinholeTimeoutArgs[3].elt = "InternalPort";
+ GetOutboundPinholeTimeoutArgs[3].val = intPort;
+ GetOutboundPinholeTimeoutArgs[4].elt = "InternalClient";
+ GetOutboundPinholeTimeoutArgs[4].val = intClient;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+ free(GetOutboundPinholeTimeoutArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout");
+ if(p)
+ *opTimeout = my_atoui(p);
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID)
+{
+ struct UPNParg * AddPinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ char * p;
+ int ret;
+
+ if(!intPort || !intClient || !proto || !remoteHost || !remotePort || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ AddPinholeArgs = calloc(7, sizeof(struct UPNParg));
+ if(AddPinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ /* RemoteHost can be wilcarded */
+ if(strncmp(remoteHost, "empty", 5)==0)
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[0].elt = "RemoteHost";
+ AddPinholeArgs[0].val = remoteHost;
+ }
+ AddPinholeArgs[1].elt = "RemotePort";
+ AddPinholeArgs[1].val = remotePort;
+ AddPinholeArgs[2].elt = "Protocol";
+ AddPinholeArgs[2].val = proto;
+ AddPinholeArgs[3].elt = "InternalPort";
+ AddPinholeArgs[3].val = intPort;
+ if(strncmp(intClient, "empty", 5)==0)
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = "";
+ }
+ else
+ {
+ AddPinholeArgs[4].elt = "InternalClient";
+ AddPinholeArgs[4].val = intClient;
+ }
+ AddPinholeArgs[5].elt = "LeaseTime";
+ AddPinholeArgs[5].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPinhole", AddPinholeArgs, &bufsize);
+ free(AddPinholeArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ p = GetValueFromNameValueList(&pdata, "UniqueID");
+ if(p)
+ {
+ strncpy(uniqueID, p, 8);
+ uniqueID[7] = '\0';
+ }
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal);*/
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime)
+{
+ struct UPNParg * UpdatePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID || !leaseTime)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg));
+ if(UpdatePinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ UpdatePinholeArgs[0].elt = "UniqueID";
+ UpdatePinholeArgs[0].val = uniqueID;
+ UpdatePinholeArgs[1].elt = "NewLeaseTime";
+ UpdatePinholeArgs[1].val = leaseTime;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "UpdatePinhole", UpdatePinholeArgs, &bufsize);
+ free(UpdatePinholeArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID)
+{
+ /*struct NameValueParserData pdata;*/
+ struct UPNParg * DeletePinholeArgs;
+ char * buffer;
+ int bufsize;
+ struct NameValueParserData pdata;
+ const char * resVal;
+ int ret;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ DeletePinholeArgs = calloc(2, sizeof(struct UPNParg));
+ if(DeletePinholeArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ DeletePinholeArgs[0].elt = "UniqueID";
+ DeletePinholeArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePinhole", DeletePinholeArgs, &bufsize);
+ free(DeletePinholeArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ /*DisplayNameValueList(buffer, bufsize);*/
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+ resVal = GetValueFromNameValueList(&pdata, "errorCode");
+ if(resVal)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * CheckPinholeWorkingArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg));
+ if(CheckPinholeWorkingArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ CheckPinholeWorkingArgs[0].elt = "UniqueID";
+ CheckPinholeWorkingArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+ free(CheckPinholeWorkingArgs);
+ if(!buffer)
+ {
+ return UPNPCOMMAND_HTTP_ERROR;
+ }
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "IsWorking");
+ if(p)
+ {
+ *isWorking=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+ else
+ *isWorking = 0;
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets)
+{
+ struct NameValueParserData pdata;
+ struct UPNParg * GetPinholePacketsArgs;
+ char * buffer;
+ int bufsize;
+ char * p;
+ int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+ if(!uniqueID)
+ return UPNPCOMMAND_INVALID_ARGS;
+
+ GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg));
+ if(GetPinholePacketsArgs == NULL)
+ return UPNPCOMMAND_MEM_ALLOC_ERROR;
+ GetPinholePacketsArgs[0].elt = "UniqueID";
+ GetPinholePacketsArgs[0].val = uniqueID;
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+ free(GetPinholePacketsArgs);
+ if(!buffer)
+ return UPNPCOMMAND_HTTP_ERROR;
+ ParseNameValue(buffer, bufsize, &pdata);
+ free(buffer); buffer = NULL;
+
+ p = GetValueFromNameValueList(&pdata, "PinholePackets");
+ if(p)
+ {
+ *packets=my_atoui(p);
+ ret = UPNPCOMMAND_SUCCESS;
+ }
+
+ p = GetValueFromNameValueList(&pdata, "errorCode");
+ if(p)
+ {
+ ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ sscanf(p, "%d", &ret);
+ }
+
+ ClearNameValueList(&pdata);
+ return ret;
+}
+
+
diff --git a/thirdparty/miniupnpc/upnpcommands.h b/thirdparty/miniupnpc/upnpcommands.h
new file mode 100644
index 00000000000..0c6d501666a
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpcommands.h
@@ -0,0 +1,348 @@
+/* $Id: upnpcommands.h,v 1.32 2018/03/13 23:34:47 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2018 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef UPNPCOMMANDS_H_INCLUDED
+#define UPNPCOMMANDS_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+#include "miniupnpctypes.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+#define UPNPCOMMAND_HTTP_ERROR (-3)
+#define UPNPCOMMAND_INVALID_RESPONSE (-4)
+#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct PortMappingParserData;
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+ const char * servicetype);
+
+MINIUPNP_LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+ const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+ const char * servicetype,
+ char * status,
+ unsigned int * uptime,
+ char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+MINIUPNP_LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+ const char * servicetype,
+ char * connectionType);
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control. */
+MINIUPNP_LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+ const char * servicetype,
+ char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+MINIUPNP_LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+ const char* servicetype,
+ unsigned int * bitrateDown,
+ unsigned int * bitrateUp);
+
+/* UPNP_AddPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ * with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ * must be the same
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ * permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ * and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ * cannot be a specific port value
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration);
+
+/* UPNP_AddAnyPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ *
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization and
+ * the sender was not authorized.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 728 NoPortMapsAvailable - There are not enough free ports available to
+ * complete port mapping.
+ * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
+ * due to conflict with other mechanisms.
+ * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
+ */
+MINIUPNP_LIBSPEC int
+UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort,
+ const char * inPort,
+ const char * inClient,
+ const char * desc,
+ const char * proto,
+ const char * remoteHost,
+ const char * leaseDuration,
+ char * reservedPort);
+
+/* UPNP_DeletePortMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+ const char * extPort, const char * proto,
+ const char * remoteHost);
+
+/* UPNP_DeletePortRangeMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 730 PortMappingNotFound - This error message is returned if no port
+ * mapping is found in the specified range.
+ * 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */
+MINIUPNP_LIBSPEC int
+UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
+ const char * extPortStart, const char * extPortEnd,
+ const char * proto,
+ const char * manage);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+MINIUPNP_LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
+ const char* servicetype,
+ unsigned int * num);
+
+/* UPNP_GetSpecificPortMappingEntry()
+ * retrieves an existing port mapping
+ * params :
+ * in extPort
+ * in proto
+ * in remoteHost
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out leaseDuration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * List of possible UPnP errors for _GetSpecificPortMappingEntry :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * extPort,
+ const char * proto,
+ const char * remoteHost,
+ char * intClient,
+ char * intPort,
+ char * desc,
+ char * enabled,
+ char * leaseDuration);
+
+/* UPNP_GetGenericPortMappingEntry()
+ * params :
+ * in index
+ * out extPort (6 bytes)
+ * out intClient (16 bytes)
+ * out intPort (6 bytes)
+ * out protocol (4 bytes)
+ * out desc (80 bytes)
+ * out enabled (4 bytes)
+ * out rHost (64 bytes)
+ * out duration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * Possible UPNP Error codes :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 606 Action not authorized - The action requested REQUIRES authorization
+ * and the sender was not authorized.
+ * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+ const char * servicetype,
+ const char * index,
+ char * extPort,
+ char * intClient,
+ char * intPort,
+ char * protocol,
+ char * desc,
+ char * enabled,
+ char * rHost,
+ char * duration);
+
+/* UPNP_GetListOfPortMappings() Available in IGD v2
+ *
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ * consistent.
+ */
+MINIUPNP_LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+ const char * servicetype,
+ const char * startPort,
+ const char * endPort,
+ const char * protocol,
+ const char * numberOfPorts,
+ struct PortMappingParserData * data);
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+MINIUPNP_LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+ const char * servicetype,
+ int * firewallEnabled,
+ int * inboundPinholeAllowed);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ int * opTimeout);
+
+MINIUPNP_LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+ const char * remoteHost,
+ const char * remotePort,
+ const char * intClient,
+ const char * intPort,
+ const char * proto,
+ const char * leaseTime,
+ char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+ const char * uniqueID,
+ const char * leaseTime);
+
+MINIUPNP_LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
+
+MINIUPNP_LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * isWorking);
+
+MINIUPNP_LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+ const char * uniqueID, int * packets);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/thirdparty/miniupnpc/upnpdev.c b/thirdparty/miniupnpc/upnpdev.c
new file mode 100644
index 00000000000..d89a9934c33
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpdev.c
@@ -0,0 +1,23 @@
+/* $Id: upnpdev.c,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2015 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#include
+#include "upnpdev.h"
+
+/* freeUPNPDevlist() should be used to
+ * free the chained list returned by upnpDiscover() */
+void freeUPNPDevlist(struct UPNPDev * devlist)
+{
+ struct UPNPDev * next;
+ while(devlist)
+ {
+ next = devlist->pNext;
+ free(devlist);
+ devlist = next;
+ }
+}
+
diff --git a/thirdparty/miniupnpc/upnpdev.h b/thirdparty/miniupnpc/upnpdev.h
new file mode 100644
index 00000000000..f4ae1744260
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpdev.h
@@ -0,0 +1,36 @@
+/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
+/* Project : miniupnp
+ * Web : http://miniupnp.free.fr/
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2018 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#ifndef UPNPDEV_H_INCLUDED
+#define UPNPDEV_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct UPNPDev {
+ struct UPNPDev * pNext;
+ char * descURL;
+ char * st;
+ char * usn;
+ unsigned int scope_id;
+ char buffer[3];
+};
+
+/* freeUPNPDevlist()
+ * free list returned by upnpDiscover() */
+MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* UPNPDEV_H_INCLUDED */
diff --git a/thirdparty/miniupnpc/upnperrors.c b/thirdparty/miniupnpc/upnperrors.c
new file mode 100644
index 00000000000..40a2e7857f4
--- /dev/null
+++ b/thirdparty/miniupnpc/upnperrors.c
@@ -0,0 +1,107 @@
+/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2007 Thomas Bernard
+ * All Right reserved.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include
+#include "upnperrors.h"
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+
+const char * strupnperror(int err)
+{
+ const char * s = NULL;
+ switch(err) {
+ case UPNPCOMMAND_SUCCESS:
+ s = "Success";
+ break;
+ case UPNPCOMMAND_UNKNOWN_ERROR:
+ s = "Miniupnpc Unknown Error";
+ break;
+ case UPNPCOMMAND_INVALID_ARGS:
+ s = "Miniupnpc Invalid Arguments";
+ break;
+ case UPNPCOMMAND_INVALID_RESPONSE:
+ s = "Miniupnpc Invalid response";
+ break;
+ case UPNPDISCOVER_SOCKET_ERROR:
+ s = "Miniupnpc Socket error";
+ break;
+ case UPNPDISCOVER_MEMORY_ERROR:
+ s = "Miniupnpc Memory allocation error";
+ break;
+ case 401:
+ s = "Invalid Action";
+ break;
+ case 402:
+ s = "Invalid Args";
+ break;
+ case 501:
+ s = "Action Failed";
+ break;
+ case 606:
+ s = "Action not authorized";
+ break;
+ case 701:
+ s = "PinholeSpaceExhausted";
+ break;
+ case 702:
+ s = "FirewallDisabled";
+ break;
+ case 703:
+ s = "InboundPinholeNotAllowed";
+ break;
+ case 704:
+ s = "NoSuchEntry";
+ break;
+ case 705:
+ s = "ProtocolNotSupported";
+ break;
+ case 706:
+ s = "InternalPortWildcardingNotAllowed";
+ break;
+ case 707:
+ s = "ProtocolWildcardingNotAllowed";
+ break;
+ case 708:
+ s = "WildcardNotPermittedInSrcIP";
+ break;
+ case 709:
+ s = "NoPacketSent";
+ break;
+ case 713:
+ s = "SpecifiedArrayIndexInvalid";
+ break;
+ case 714:
+ s = "NoSuchEntryInArray";
+ break;
+ case 715:
+ s = "WildCardNotPermittedInSrcIP";
+ break;
+ case 716:
+ s = "WildCardNotPermittedInExtPort";
+ break;
+ case 718:
+ s = "ConflictInMappingEntry";
+ break;
+ case 724:
+ s = "SamePortValuesRequired";
+ break;
+ case 725:
+ s = "OnlyPermanentLeasesSupported";
+ break;
+ case 726:
+ s = "RemoteHostOnlySupportsWildcard";
+ break;
+ case 727:
+ s = "ExternalPortOnlySupportsWildcard";
+ break;
+ default:
+ s = "UnknownError";
+ break;
+ }
+ return s;
+}
diff --git a/thirdparty/miniupnpc/upnperrors.h b/thirdparty/miniupnpc/upnperrors.h
new file mode 100644
index 00000000000..8499d9a1c9b
--- /dev/null
+++ b/thirdparty/miniupnpc/upnperrors.h
@@ -0,0 +1,26 @@
+/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */
+/* (c) 2007-2015 Thomas Bernard
+ * All rights reserved.
+ * MiniUPnP Project.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef UPNPERRORS_H_INCLUDED
+#define UPNPERRORS_H_INCLUDED
+
+#include "miniupnpc_declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* strupnperror()
+ * Return a string description of the UPnP error code
+ * or NULL for undefinded errors */
+MINIUPNP_LIBSPEC const char * strupnperror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/thirdparty/miniupnpc/upnpreplyparse.c b/thirdparty/miniupnpc/upnpreplyparse.c
new file mode 100644
index 00000000000..68a47c02787
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpreplyparse.c
@@ -0,0 +1,196 @@
+/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2017 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#include
+#include
+#include
+
+#include "upnpreplyparse.h"
+#include "minixml.h"
+
+static void
+NameValueParserStartElt(void * d, const char * name, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ data->topelt = 1;
+ if(l>63)
+ l = 63;
+ memcpy(data->curelt, name, l);
+ data->curelt[l] = '\0';
+ data->cdata = NULL;
+ data->cdatalen = 0;
+}
+
+static void
+NameValueParserEndElt(void * d, const char * name, int namelen)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ struct NameValue * nv;
+ (void)name;
+ (void)namelen;
+ if(!data->topelt)
+ return;
+ if(strcmp(data->curelt, "NewPortListing") != 0)
+ {
+ int l;
+ /* standard case. Limited to n chars strings */
+ l = data->cdatalen;
+ nv = malloc(sizeof(struct NameValue));
+ if(nv == NULL)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "NameValueParserEndElt");
+#endif /* DEBUG */
+ return;
+ }
+ if(l>=(int)sizeof(nv->value))
+ l = sizeof(nv->value) - 1;
+ strncpy(nv->name, data->curelt, 64);
+ nv->name[63] = '\0';
+ if(data->cdata != NULL)
+ {
+ memcpy(nv->value, data->cdata, l);
+ nv->value[l] = '\0';
+ }
+ else
+ {
+ nv->value[0] = '\0';
+ }
+ nv->l_next = data->l_head; /* insert in list */
+ data->l_head = nv;
+ }
+ data->cdata = NULL;
+ data->cdatalen = 0;
+ data->topelt = 0;
+}
+
+static void
+NameValueParserGetData(void * d, const char * datas, int l)
+{
+ struct NameValueParserData * data = (struct NameValueParserData *)d;
+ if(strcmp(data->curelt, "NewPortListing") == 0)
+ {
+ /* specific case for NewPortListing which is a XML Document */
+ data->portListing = malloc(l + 1);
+ if(!data->portListing)
+ {
+ /* malloc error */
+#ifdef DEBUG
+ fprintf(stderr, "%s: error allocating memory",
+ "NameValueParserGetData");
+#endif /* DEBUG */
+ return;
+ }
+ memcpy(data->portListing, datas, l);
+ data->portListing[l] = '\0';
+ data->portListingLength = l;
+ }
+ else
+ {
+ /* standard case. */
+ data->cdata = datas;
+ data->cdatalen = l;
+ }
+}
+
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data)
+{
+ struct xmlparser parser;
+ memset(data, 0, sizeof(struct NameValueParserData));
+ /* init xmlparser object */
+ parser.xmlstart = buffer;
+ parser.xmlsize = bufsize;
+ parser.data = data;
+ parser.starteltfunc = NameValueParserStartElt;
+ parser.endeltfunc = NameValueParserEndElt;
+ parser.datafunc = NameValueParserGetData;
+ parser.attfunc = 0;
+ parsexml(&parser);
+}
+
+void
+ClearNameValueList(struct NameValueParserData * pdata)
+{
+ struct NameValue * nv;
+ if(pdata->portListing)
+ {
+ free(pdata->portListing);
+ pdata->portListing = NULL;
+ pdata->portListingLength = 0;
+ }
+ while((nv = pdata->l_head) != NULL)
+ {
+ pdata->l_head = nv->l_next;
+ free(nv);
+ }
+}
+
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ for(nv = pdata->l_head;
+ (nv != NULL) && (p == NULL);
+ nv = nv->l_next)
+ {
+ if(strcmp(nv->name, Name) == 0)
+ p = nv->value;
+ }
+ return p;
+}
+
+#if 0
+/* useless now that minixml ignores namespaces by itself */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ char * pname;
+ for(nv = pdata->head.lh_first;
+ (nv != NULL) && (p == NULL);
+ nv = nv->entries.le_next)
+ {
+ pname = strrchr(nv->name, ':');
+ if(pname)
+ pname++;
+ else
+ pname = nv->name;
+ if(strcmp(pname, Name)==0)
+ p = nv->value;
+ }
+ return p;
+}
+#endif
+
+/* debug all-in-one function
+ * do parsing then display to stdout */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize)
+{
+ struct NameValueParserData pdata;
+ struct NameValue * nv;
+ ParseNameValue(buffer, bufsize, &pdata);
+ for(nv = pdata.l_head;
+ nv != NULL;
+ nv = nv->l_next)
+ {
+ printf("%s = %s\n", nv->name, nv->value);
+ }
+ ClearNameValueList(&pdata);
+}
+#endif /* DEBUG */
+
diff --git a/thirdparty/miniupnpc/upnpreplyparse.h b/thirdparty/miniupnpc/upnpreplyparse.h
new file mode 100644
index 00000000000..6badd15b26a
--- /dev/null
+++ b/thirdparty/miniupnpc/upnpreplyparse.h
@@ -0,0 +1,63 @@
+/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2013 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#ifndef UPNPREPLYPARSE_H_INCLUDED
+#define UPNPREPLYPARSE_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct NameValue {
+ struct NameValue * l_next;
+ char name[64];
+ char value[128];
+};
+
+struct NameValueParserData {
+ struct NameValue * l_head;
+ char curelt[64];
+ char * portListing;
+ int portListingLength;
+ int topelt;
+ const char * cdata;
+ int cdatalen;
+};
+
+/* ParseNameValue() */
+void
+ParseNameValue(const char * buffer, int bufsize,
+ struct NameValueParserData * data);
+
+/* ClearNameValueList() */
+void
+ClearNameValueList(struct NameValueParserData * pdata);
+
+/* GetValueFromNameValueList() */
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+ const char * Name);
+
+#if 0
+/* GetValueFromNameValueListIgnoreNS() */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name);
+#endif
+
+/* DisplayNameValueList() */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
]