[PATCH 2/7] hnetcfg: Get port mapping list in init_gateway_connection().
Paul Gofman
pgofman at codeweavers.com
Wed Feb 2 02:32:54 CST 2022
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
dlls/hnetcfg/port.c | 306 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 306 insertions(+)
diff --git a/dlls/hnetcfg/port.c b/dlls/hnetcfg/port.c
index 389e36f4d50..070ba1ef7ab 100644
--- a/dlls/hnetcfg/port.c
+++ b/dlls/hnetcfg/port.c
@@ -40,6 +40,23 @@
WINE_DEFAULT_DEBUG_CHANNEL(hnetcfg);
+struct port_mapping
+{
+ BSTR external_ip;
+ LONG external;
+ BSTR protocol;
+ LONG internal;
+ BSTR client;
+ VARIANT_BOOL enabled;
+ BSTR descr;
+};
+
+struct xml_value_desc
+{
+ const WCHAR *name;
+ BSTR value;
+};
+
static struct
{
LONG refs;
@@ -49,11 +66,32 @@ static struct
WCHAR desc_urlpath[128];
WCHAR control_url[256];
unsigned int version;
+ struct port_mapping *mappings;
+ unsigned int mapping_count;
}
upnp_gateway_connection;
static SRWLOCK upnp_gateway_connection_lock = SRWLOCK_INIT;
+static void free_port_mapping( struct port_mapping *mapping )
+{
+ SysFreeString( mapping->external_ip );
+ SysFreeString( mapping->protocol );
+ SysFreeString( mapping->client );
+ SysFreeString( mapping->descr );
+}
+
+static void free_mappings(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < upnp_gateway_connection.mapping_count; ++i)
+ free_port_mapping( &upnp_gateway_connection.mappings[i] );
+ free( upnp_gateway_connection.mappings );
+ upnp_gateway_connection.mappings = NULL;
+ upnp_gateway_connection.mapping_count = 0;
+}
+
static BOOL parse_search_response( char *response, WCHAR *locationW, unsigned int location_size )
{
char *saveptr = NULL, *tok, *tok2;
@@ -171,6 +209,64 @@ done:
return ret;
}
+static BOOL get_xml_elements( const char *desc_xml, struct xml_value_desc *values, unsigned int value_count )
+{
+ XmlNodeType node_type;
+ IXmlReader *reader;
+ const WCHAR *value;
+ BOOL ret = FALSE;
+ IStream *stream;
+ unsigned int i;
+ HRESULT hr;
+
+ for (i = 0; i < value_count; ++i) assert( !values[i].value );
+
+ if (!(stream = SHCreateMemStream( (BYTE *)desc_xml, strlen( desc_xml ) + 1 ))) return FALSE;
+ if (FAILED(hr = CreateXmlReader( &IID_IXmlReader, (void **)&reader, NULL )))
+ {
+ IStream_Release( stream );
+ return FALSE;
+ }
+ if (FAILED(hr = IXmlReader_SetInput( reader, (IUnknown*)stream ))) goto done;
+
+ while (SUCCEEDED(IXmlReader_Read( reader, &node_type )) && node_type != XmlNodeType_None)
+ {
+ if (node_type != XmlNodeType_Element) continue;
+
+ if (FAILED(IXmlReader_GetQualifiedName( reader, &value, NULL ))) goto done;
+ for (i = 0; i < value_count; ++i)
+ if (!wcsicmp( value, values[i].name )) break;
+ if (i == value_count) continue;
+ if (FAILED(IXmlReader_Read(reader, &node_type ))) goto done;
+ if (node_type != XmlNodeType_Text)
+ {
+ if (node_type == XmlNodeType_EndElement) value = L"";
+ else goto done;
+ }
+ if (FAILED(IXmlReader_GetValue( reader, &value, NULL ))) goto done;
+ if (values[i].value)
+ {
+ WARN( "Duplicate value %s.\n", debugstr_w(values[i].name) );
+ goto done;
+ }
+ if (!(values[i].value = SysAllocString( value ))) goto done;
+ }
+ ret = TRUE;
+
+done:
+ if (!ret)
+ {
+ for (i = 0; i < value_count; ++i)
+ {
+ SysFreeString( values[i].value );
+ values[i].value = NULL;
+ }
+ }
+ IXmlReader_Release( reader );
+ IStream_Release( stream );
+ return ret;
+}
+
static BOOL open_gateway_connection(void)
{
static const int timeout = 3000;
@@ -270,12 +366,220 @@ static BOOL get_control_url(void)
static void gateway_connection_cleanup(void)
{
TRACE( ".\n" );
+ free_mappings();
WinHttpCloseHandle( upnp_gateway_connection.connection );
WinHttpCloseHandle( upnp_gateway_connection.session );
if (upnp_gateway_connection.winsock_initialized) WSACleanup();
memset( &upnp_gateway_connection, 0, sizeof(upnp_gateway_connection) );
}
+static BOOL request_service( const WCHAR *function, const struct xml_value_desc *request_param,
+ unsigned int request_param_count, struct xml_value_desc *result,
+ unsigned int result_count, DWORD *http_status, BSTR *server_error_code_str )
+{
+ static const char request_template_header[] =
+ "<?xml version=\"1.0\"?>\r\n"
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <SOAP-ENV:Body>\r\n"
+ " <m:%S xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:%u\">\r\n";
+ static const char request_template_footer[] =
+ " </m:%S>\r\n"
+ " </SOAP-ENV:Body>\r\n"
+ "</SOAP-ENV:Envelope>\r\n";
+
+ unsigned int request_data_size, request_len, offset, i, reply_buffer_size;
+ char *request_data, *reply_buffer = NULL, *ptr;
+ struct xml_value_desc error_value_desc;
+ WCHAR request_headers[1024];
+ HINTERNET request = NULL;
+ BOOL ret = FALSE;
+ DWORD size;
+
+ *server_error_code_str = NULL;
+ request_data_size = strlen(request_template_header) + strlen(request_template_footer) + 2 * wcslen( function )
+ + 9 /* version + zero terminator */;
+ for (i = 0; i < request_param_count; ++i)
+ {
+ request_data_size += 13 + 2 * wcslen( request_param[i].name ) + wcslen( request_param[i].value );
+ }
+ if (!(request_data = malloc( request_data_size ))) return FALSE;
+
+ request = WinHttpOpenRequest( upnp_gateway_connection.connection, L"POST", upnp_gateway_connection.control_url,
+ NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0 );
+ if (!request) goto done;
+
+ ptr = request_data;
+ snprintf( ptr, request_data_size, request_template_header, function, upnp_gateway_connection.version );
+ offset = strlen( ptr );
+ ptr += offset;
+ request_data_size -= offset;
+ for (i = 0; i < request_param_count; ++i)
+ {
+ snprintf( ptr, request_data_size, " <%S>%S</%S>\r\n",
+ request_param[i].name, request_param[i].value, request_param[i].name);
+ offset = strlen( ptr );
+ ptr += offset;
+ request_data_size -= offset;
+ }
+ snprintf( ptr, request_data_size, request_template_footer, function );
+
+ request_len = strlen( request_data );
+ swprintf( request_headers, ARRAY_SIZE(request_headers),
+ L"SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:%u#%s\"\r\n"
+ L"Content-Type: text/xml",
+ upnp_gateway_connection.version, function );
+ if (!WinHttpSendRequest( request, request_headers, -1, request_data, request_len, request_len, 0 ))
+ {
+ WARN( "Error sending request %u.\n", GetLastError() );
+ goto done;
+ }
+ if (!WinHttpReceiveResponse(request, NULL))
+ {
+ WARN( "Error receiving response %u.\n", GetLastError() );
+ goto done;
+ }
+ size = sizeof(*http_status);
+ if (!WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
+ NULL, http_status, &size, NULL) || *http_status != HTTP_STATUS_OK )
+ {
+ if (*http_status != HTTP_STATUS_SERVER_ERROR)
+ {
+ ret = TRUE;
+ goto done;
+ }
+ }
+
+ offset = 0;
+ reply_buffer_size = 1024;
+ if (!(reply_buffer = malloc( reply_buffer_size ))) goto done;
+ while ((ret = WinHttpReadData( request, reply_buffer + offset, reply_buffer_size - offset - 1, &size )) && size)
+ {
+ offset += size;
+ if (offset + 1 == reply_buffer_size)
+ {
+ char *new;
+
+ reply_buffer_size *= 2;
+ if (!(new = realloc( reply_buffer, reply_buffer_size ))) goto done;
+ reply_buffer = new;
+ }
+ }
+ reply_buffer[offset] = 0;
+
+ if (*http_status == HTTP_STATUS_OK) ret = get_xml_elements( reply_buffer, result, result_count );
+ else
+ {
+ error_value_desc.name = L"errorCode";
+ error_value_desc.value = NULL;
+ if ((ret = get_xml_elements( reply_buffer, &error_value_desc, 1 )))
+ *server_error_code_str = error_value_desc.value;
+ }
+
+done:
+ free( reply_buffer );
+ free( request_data );
+ WinHttpCloseHandle( request );
+ return ret;
+}
+
+enum port_mapping_parameter
+{
+ PM_EXTERNAL_IP,
+ PM_EXTERNAL,
+ PM_PROTOCOL,
+ PM_INTERNAL,
+ PM_CLIENT,
+ PM_ENABLED,
+ PM_DESC,
+ PM_LEASE_DURATION,
+ PM_LAST
+};
+
+static struct xml_value_desc port_mapping_template[] =
+{
+ { L"NewRemoteHost" },
+ { L"NewExternalPort" },
+ { L"NewProtocol" },
+ { L"NewInternalPort" },
+ { L"NewInternalClient" },
+ { L"NewEnabled" },
+ { L"NewPortMappingDescription" },
+ { L"NewLeaseDuration" },
+};
+
+static LONG long_from_bstr( BSTR s )
+{
+ if (!s) return 0;
+ return _wtoi( s );
+}
+
+static void update_mapping_list(void)
+{
+ struct xml_value_desc mapping_desc[ARRAY_SIZE(port_mapping_template)];
+ struct xml_value_desc index_param;
+ struct port_mapping *new_mappings;
+ unsigned int i, index;
+ WCHAR index_str[9];
+ BSTR error_str;
+ DWORD status;
+ BOOL ret;
+
+ free_mappings();
+
+ index_param.name = L"NewPortMappingIndex";
+
+ index = 0;
+ while (1)
+ {
+ new_mappings = realloc( upnp_gateway_connection.mappings, (index + 1) * sizeof(*new_mappings) );
+ if (!new_mappings) break;
+ upnp_gateway_connection.mappings = new_mappings;
+
+ memcpy( mapping_desc, port_mapping_template, sizeof(mapping_desc) );
+ swprintf( index_str, ARRAY_SIZE(index_str), L"%u", index );
+ index_param.value = SysAllocString( index_str );
+ ret = request_service( L"GetGenericPortMappingEntry", &index_param, 1,
+ mapping_desc, ARRAY_SIZE(mapping_desc), &status, &error_str );
+ SysFreeString( index_param.value );
+ if (!ret) break;
+ if (status != HTTP_STATUS_OK)
+ {
+ if (error_str)
+ {
+ if (long_from_bstr( error_str ) != 713)
+ WARN( "Server returned error %s.\n", debugstr_w(error_str) );
+ SysFreeString( error_str );
+ }
+ break;
+ }
+ new_mappings[index].external_ip = mapping_desc[PM_EXTERNAL_IP].value;
+ mapping_desc[PM_EXTERNAL_IP].value = NULL;
+ new_mappings[index].external = long_from_bstr( mapping_desc[PM_EXTERNAL].value );
+ new_mappings[index].protocol = mapping_desc[PM_PROTOCOL].value;
+ mapping_desc[PM_PROTOCOL].value = NULL;
+ new_mappings[index].internal = long_from_bstr( mapping_desc[PM_INTERNAL].value );
+ new_mappings[index].client = mapping_desc[PM_CLIENT].value;
+ mapping_desc[PM_CLIENT].value = NULL;
+ if (!wcsicmp( mapping_desc[PM_ENABLED].value, L"true" ) || long_from_bstr( mapping_desc[PM_ENABLED].value ))
+ new_mappings[index].enabled = VARIANT_TRUE;
+ else
+ new_mappings[index].enabled = VARIANT_FALSE;
+ new_mappings[index].descr = mapping_desc[PM_DESC].value;
+ mapping_desc[PM_DESC].value = NULL;
+
+ TRACE( "%s %s %s:%u -> %s:%u, enabled %d.\n", debugstr_w(new_mappings[index].descr),
+ debugstr_w(new_mappings[index].protocol), debugstr_w(new_mappings[index].external_ip),
+ new_mappings[index].external, debugstr_w(new_mappings[index].client),
+ new_mappings[index].internal, new_mappings[index].enabled );
+
+ for (i = 0; i < ARRAY_SIZE(mapping_desc); ++i)
+ SysFreeString( mapping_desc[i].value );
+ upnp_gateway_connection.mappings = new_mappings;
+ upnp_gateway_connection.mapping_count = ++index;
+ }
+}
+
static BOOL init_gateway_connection(void)
{
static const char upnp_search_request[] =
@@ -363,6 +667,8 @@ static BOOL init_gateway_connection(void)
}
TRACE( "control_url %s, version %u.\n", debugstr_w(upnp_gateway_connection.control_url),
upnp_gateway_connection.version );
+
+ update_mapping_list();
return TRUE;
}
--
2.34.1
More information about the wine-devel
mailing list