diff options
author | mhilbrunner <m.hilbrunner@gmail.com> | 2018-05-11 02:00:16 +0200 |
---|---|---|
committer | mhilbrunner <m.hilbrunner@gmail.com> | 2018-06-07 01:52:04 +0200 |
commit | c21da40de5fb20ade5b07dcc961c9364ba3815af (patch) | |
tree | 0afa8202296f30dcd939bc24492e97d4864f4fb1 /modules | |
parent | e854a71fac776cf49a0d65468e8130401eff3c6b (diff) |
Add UPnP support (port forwarding, querying external IP)
Diffstat (limited to 'modules')
-rw-r--r-- | modules/upnp/SCsub | 32 | ||||
-rw-r--r-- | modules/upnp/config.py | 14 | ||||
-rw-r--r-- | modules/upnp/doc_classes/UPNP.xml | 227 | ||||
-rw-r--r-- | modules/upnp/doc_classes/UPNPDevice.xml | 109 | ||||
-rw-r--r-- | modules/upnp/register_types.cpp | 43 | ||||
-rw-r--r-- | modules/upnp/register_types.h | 32 | ||||
-rw-r--r-- | modules/upnp/upnp.cpp | 401 | ||||
-rw-r--r-- | modules/upnp/upnp.h | 124 | ||||
-rw-r--r-- | modules/upnp/upnpdevice.cpp | 197 | ||||
-rw-r--r-- | modules/upnp/upnpdevice.h | 95 |
10 files changed, 1274 insertions, 0 deletions
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub new file mode 100644 index 0000000000..cde231867f --- /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 0000000000..8724ff1a51 --- /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 0000000000..30be9c836b --- /dev/null +++ b/modules/upnp/doc_classes/UPNP.xml @@ -0,0 +1,227 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="UPNP" inherits="Reference" category="Core" version="3.1"> + <brief_description> + UPNP network functions. + </brief_description> + <description> + 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. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_device"> + <return type="void"> + </return> + <argument index="0" name="device" type="UPNPDevice"> + </argument> + <description> + Adds the given [UPNPDevice] to the list of discovered devices. + </description> + </method> + <method name="add_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="port_internal" type="int" default="0"> + </argument> + <argument index="2" name="desc" type="String" default=""""> + </argument> + <argument index="3" name="proto" type="String" default=""UDP""> + </argument> + <argument index="4" name="duration" type="int" default="0"> + </argument> + <description> + 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. + </description> + </method> + <method name="clear_devices"> + <return type="void"> + </return> + <description> + Clears the list of discovered devices. + </description> + </method> + <method name="delete_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="proto" type="String" default=""UDP""> + </argument> + <description> + 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. + </description> + </method> + <method name="discover"> + <return type="int"> + </return> + <argument index="0" name="timeout" type="int" default="2000"> + </argument> + <argument index="1" name="ttl" type="int" default="2"> + </argument> + <argument index="2" name="device_filter" type="String" default=""InternetGatewayDevice""> + </argument> + <description> + 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. + </description> + </method> + <method name="get_device" qualifiers="const"> + <return type="UPNPDevice"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + Returns the [UPNPDevice] at the given [code]index[/code]. + </description> + </method> + <method name="get_device_count" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the number of discovered [UPNPDevice]s. + </description> + </method> + <method name="get_gateway" qualifiers="const"> + <return type="UPNPDevice"> + </return> + <description> + Returns the default gateway. That is the first discovered [UPNPDevice] that is also a valid IGD (InternetGatewayDevice). + </description> + </method> + <method name="query_external_address" qualifiers="const"> + <return type="String"> + </return> + <description> + Returns the external [IP] address of the default gateway (see [method get_gateway]) as string. Returns an empty string on error. + </description> + </method> + <method name="remove_device"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + Removes the device at [code]index[/code] from the list of discovered devices. + </description> + </method> + <method name="set_device"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <argument index="1" name="device" type="UPNPDevice"> + </argument> + <description> + Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code]. + </description> + </method> + </methods> + <members> + <member name="discover_ipv6" type="bool" setter="set_discover_ipv6" getter="is_discover_ipv6"> + If [code]true[/code], IPv6 is used for [UPNPDevice] discovery. + </member> + <member name="discover_local_port" type="int" setter="set_discover_local_port" getter="get_discover_local_port"> + 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. + </member> + <member name="discover_multicast_if" type="String" setter="set_discover_multicast_if" getter="get_discover_multicast_if"> + Multicast interface to use for discovery. Uses the default multicast interface if empty. + </member> + </members> + <constants> + <constant name="UPNP_RESULT_SUCCESS" value="0" enum="UPNPResult"> + UPNP command or discovery was successful. + </constant> + <constant name="UPNP_RESULT_NOT_AUTHORIZED" value="1" enum="UPNPResult"> + Not authorized to use the command on the [UPNPDevice]. May be returned when the user disabled UPNP on their router. + </constant> + <constant name="UPNP_RESULT_PORT_MAPPING_NOT_FOUND" value="2" enum="UPNPResult"> + No port mapping was found for the given port, protocol combination on the given [UPNPDevice]. + </constant> + <constant name="UPNP_RESULT_INCONSISTENT_PARAMETERS" value="3" enum="UPNPResult"> + Inconsistent parameters. + </constant> + <constant name="UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY" value="4" enum="UPNPResult"> + No such entry in array. May be returned if a given port, protocol combination is not found on an [UPNPDevice]. + </constant> + <constant name="UPNP_RESULT_ACTION_FAILED" value="5" enum="UPNPResult"> + The action failed. + </constant> + <constant name="UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED" value="6" enum="UPNPResult"> + The [UPNPDevice] does not allow wildcard values for the source IP address. + </constant> + <constant name="UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED" value="7" enum="UPNPResult"> + The [UPNPDevice] does not allow wildcard values for the external port. + </constant> + <constant name="UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED" value="8" enum="UPNPResult"> + The [UPNPDevice] does not allow wildcard values for the internal port. + </constant> + <constant name="UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD" value="9" enum="UPNPResult"> + The remote host value must be a wildcard. + </constant> + <constant name="UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD" value="10" enum="UPNPResult"> + The external port value must be a wildcard. + </constant> + <constant name="UPNP_RESULT_NO_PORT_MAPS_AVAILABLE" value="11" enum="UPNPResult"> + No port maps are available. May also be returned if port mapping functionality is not available. + </constant> + <constant name="UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM" value="12" enum="UPNPResult"> + 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. + </constant> + <constant name="UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING" value="13" enum="UPNPResult"> + Conflict with an existing port mapping. + </constant> + <constant name="UPNP_RESULT_SAME_PORT_VALUES_REQUIRED" value="14" enum="UPNPResult"> + External and internal port values must be the same. + </constant> + <constant name="UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED" value="15" enum="UPNPResult"> + Only permanent leases are supported. Do not use the [code]duration[/code] parameter when adding port mappings. + </constant> + <constant name="UPNP_RESULT_INVALID_GATEWAY" value="16" enum="UPNPResult"> + Invalid gateway. + </constant> + <constant name="UPNP_RESULT_INVALID_PORT" value="17" enum="UPNPResult"> + Invalid port. + </constant> + <constant name="UPNP_RESULT_INVALID_PROTOCOL" value="18" enum="UPNPResult"> + Invalid protocol. + </constant> + <constant name="UPNP_RESULT_INVALID_DURATION" value="19" enum="UPNPResult"> + Invalid duration. + </constant> + <constant name="UPNP_RESULT_INVALID_ARGS" value="20" enum="UPNPResult"> + Invalid arguments. + </constant> + <constant name="UPNP_RESULT_INVALID_RESPONSE" value="21" enum="UPNPResult"> + Invalid response. + </constant> + <constant name="UPNP_RESULT_INVALID_PARAM" value="22" enum="UPNPResult"> + Invalid parameter. + </constant> + <constant name="UPNP_RESULT_HTTP_ERROR" value="23" enum="UPNPResult"> + HTTP error. + </constant> + <constant name="UPNP_RESULT_SOCKET_ERROR" value="24" enum="UPNPResult"> + Socket error. + </constant> + <constant name="UPNP_RESULT_MEM_ALLOC_ERROR" value="25" enum="UPNPResult"> + Error allocating memory. + </constant> + <constant name="UPNP_RESULT_NO_GATEWAY" value="26" enum="UPNPResult"> + No gateway available. You may need to call [method discover] first, or discovery didn't detect any valid IGDs (InternetGatewayDevices). + </constant> + <constant name="UPNP_RESULT_NO_DEVICES" value="27" enum="UPNPResult"> + No devices available. You may need to call [method discover] first, or discovery didn't detect any valid [UPNPDevice]s. + </constant> + <constant name="UPNP_RESULT_UNKNOWN_ERROR" value="28" enum="UPNPResult"> + Unknown error. + </constant> + </constants> +</class> diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml new file mode 100644 index 0000000000..9de8042daf --- /dev/null +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="UPNPDevice" inherits="Reference" category="Core" version="3.1"> + <brief_description> + UPNP device. + </brief_description> + <description> + 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. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="port_internal" type="int" default="0"> + </argument> + <argument index="2" name="desc" type="String" default=""""> + </argument> + <argument index="3" name="proto" type="String" default=""UDP""> + </argument> + <argument index="4" name="duration" type="int" default="0"> + </argument> + <description> + 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]. + </description> + </method> + <method name="delete_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="proto" type="String" default=""UDP""> + </argument> + <description> + Deletes the port mapping identified by the given port and protocol combination on this device. See [method UPNP.delete_port_mapping]. + </description> + </method> + <method name="is_valid_gateway" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if this is a valid IGD (InternetGatewayDevice) which potentially supports port forwarding. + </description> + </method> + <method name="query_external_address" qualifiers="const"> + <return type="String"> + </return> + <description> + Returns the external IP address of this [UPNPDevice] or an empty string. + </description> + </method> + </methods> + <members> + <member name="description_url" type="String" setter="set_description_url" getter="get_description_url"> + URL to the device description. + </member> + <member name="igd_control_url" type="String" setter="set_igd_control_url" getter="get_igd_control_url"> + IDG control URL. + </member> + <member name="igd_our_addr" type="String" setter="set_igd_our_addr" getter="get_igd_our_addr"> + Address of the local machine in the network connecting it to this [UPNPDevice]. + </member> + <member name="igd_service_type" type="String" setter="set_igd_service_type" getter="get_igd_service_type"> + IGD service type. + </member> + <member name="igd_status" type="int" setter="set_igd_status" getter="get_igd_status" enum="UPNPDevice.IGDStatus"> + IGD status. See [enum IGDStatus]. + </member> + <member name="service_type" type="String" setter="set_service_type" getter="get_service_type"> + Service type. + </member> + </members> + <constants> + <constant name="IGD_STATUS_OK" value="0" enum="IGDStatus"> + OK. + </constant> + <constant name="IGD_STATUS_HTTP_ERROR" value="1" enum="IGDStatus"> + HTTP error. + </constant> + <constant name="IGD_STATUS_HTTP_EMPTY" value="2" enum="IGDStatus"> + Empty HTTP response. + </constant> + <constant name="IGD_STATUS_NO_URLS" value="3" enum="IGDStatus"> + Returned response contained no URLs. + </constant> + <constant name="IGD_STATUS_NO_IGD" value="4" enum="IGDStatus"> + Not a valid IGD. + </constant> + <constant name="IGD_STATUS_DISCONNECTED" value="5" enum="IGDStatus"> + Disconnected. + </constant> + <constant name="IGD_STATUS_UNKNOWN_DEVICE" value="6" enum="IGDStatus"> + Unknown device. + </constant> + <constant name="IGD_STATUS_INVALID_CONTROL" value="7" enum="IGDStatus"> + Invalid control. + </constant> + <constant name="IGD_STATUS_MALLOC_ERROR" value="8" enum="IGDStatus"> + Memory allocation error. + </constant> + <constant name="IGD_STATUS_UNKNOWN_ERROR" value="9" enum="IGDStatus"> + Unknown error. + </constant> + </constants> +</class> diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp new file mode 100644 index 0000000000..c79155c4d2 --- /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<UPNP>(); + ClassDB::register_class<UPNPDevice>(); +} + +void unregister_upnp_types() { +} diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h new file mode 100644 index 0000000000..2aeb06abc7 --- /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 0000000000..32fdfe22f8 --- /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 <stdlib.h> + +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<UPNPDevice> 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<UPNPDevice> 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<UPNPDevice> UPNP::get_device(int index) const { + ERR_FAIL_INDEX_V(index, devices.size(), NULL); + + return devices.get(index); +} + +void UPNP::add_device(Ref<UPNPDevice> device) { + ERR_FAIL_COND(device == NULL); + + devices.push_back(device); +} + +void UPNP::set_device(int index, Ref<UPNPDevice> 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<UPNPDevice> UPNP::get_gateway() const { + ERR_FAIL_COND_V(devices.size() < 1, NULL); + + for (int i = 0; i < devices.size(); i++) { + Ref<UPNPDevice> 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<UPNPDevice> 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<UPNPDevice> 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<UPNPDevice> 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 0000000000..fb0c0f30a0 --- /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 <reference.h> + +class UPNP : public Reference { + + GDCLASS(UPNP, Reference); + +private: + String discover_multicast_if; + int discover_local_port; + bool discover_ipv6; + + Vector<Ref<UPNPDevice> > devices; + + bool is_common_device(const String &dev) const; + void add_device_to_list(UPNPDev *dev, UPNPDev *devlist); + void parse_igd(Ref<UPNPDevice> 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<UPNPDevice> get_device(int index) const; + void add_device(Ref<UPNPDevice> device); + void set_device(int index, Ref<UPNPDevice> device); + void remove_device(int index); + void clear_devices(); + + Ref<UPNPDevice> 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 0000000000..a5959cf649 --- /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 0000000000..25c9fe1ba3 --- /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 <reference.h> + +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 |