[PATCH 3/6] hnetcfg: Return stub interface from upnpnat_get_StaticPortMappingCollection() if UPNP IGD is found.

Paul Gofman pgofman at codeweavers.com
Tue Feb 1 04:49:21 CST 2022


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/hnetcfg/Makefile.in       |   2 +-
 dlls/hnetcfg/apps.c            |   3 +-
 dlls/hnetcfg/hnetcfg_private.h |   1 +
 dlls/hnetcfg/port.c            | 348 ++++++++++++++++++++++++++++++++-
 dlls/hnetcfg/tests/policy.c    |   3 +-
 5 files changed, 350 insertions(+), 7 deletions(-)

diff --git a/dlls/hnetcfg/Makefile.in b/dlls/hnetcfg/Makefile.in
index 3bc329cd7f1..8d1f1b27ffe 100644
--- a/dlls/hnetcfg/Makefile.in
+++ b/dlls/hnetcfg/Makefile.in
@@ -1,7 +1,7 @@
 EXTRADEFS = -DWINE_NO_LONG_TYPES
 MODULE    = hnetcfg.dll
 IMPORTS   = oleaut32 ole32 advapi32 mpr uuid
-
+DELAYIMPORTS = ws2_32
 EXTRADLLFLAGS = -Wb,--prefer-native
 
 C_SRCS = \
diff --git a/dlls/hnetcfg/apps.c b/dlls/hnetcfg/apps.c
index 16379e3457b..c5fe5e41f5e 100644
--- a/dlls/hnetcfg/apps.c
+++ b/dlls/hnetcfg/apps.c
@@ -114,7 +114,8 @@ static REFIID tid_id[] =
     &IID_INetFwPolicy,
     &IID_INetFwPolicy2,
     &IID_INetFwProfile,
-    &IID_IUPnPNAT
+    &IID_IUPnPNAT,
+    &IID_IStaticPortMappingCollection,
 };
 
 HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
diff --git a/dlls/hnetcfg/hnetcfg_private.h b/dlls/hnetcfg/hnetcfg_private.h
index be2d0f3c7c6..91fef756464 100644
--- a/dlls/hnetcfg/hnetcfg_private.h
+++ b/dlls/hnetcfg/hnetcfg_private.h
@@ -28,6 +28,7 @@ enum type_id
     INetFwProfile_tid,
     INetFwRules_tid,
     IUPnPNAT_tid,
+    IStaticPortMappingCollection_tid,
     last_tid
 };
 
diff --git a/dlls/hnetcfg/port.c b/dlls/hnetcfg/port.c
index 9e9264df1dc..89a3f571d45 100644
--- a/dlls/hnetcfg/port.c
+++ b/dlls/hnetcfg/port.c
@@ -24,6 +24,9 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winuser.h"
+#include "string.h"
+#include "assert.h"
+#include "winsock2.h"
 #include "ole2.h"
 #include "netfw.h"
 #include "natupnp.h"
@@ -33,6 +36,343 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(hnetcfg);
 
+static struct
+{
+    LONG refs;
+    BOOL winsock_initialized;
+}
+upnp_gateway_connection;
+
+static SRWLOCK upnp_gateway_connection_lock = SRWLOCK_INIT;
+
+static void gateway_connection_cleanup(void)
+{
+    TRACE( ".\n" );
+    if (upnp_gateway_connection.winsock_initialized) WSACleanup();
+    memset( &upnp_gateway_connection, 0, sizeof(upnp_gateway_connection) );
+}
+
+static BOOL init_gateway_connection(void)
+{
+    static const char upnp_search_request[] =
+        "M-SEARCH * HTTP/1.1\r\n"
+        "HOST:239.255.255.250:1900\r\n"
+        "ST:upnp:rootdevice\r\n"
+        "MX:2\r\n"
+        "MAN:\"ssdp:discover\"\r\n"
+        "\r\n";
+
+    const DWORD timeout = 1000;
+    int len, address_len;
+    char buffer[2048];
+    SOCKADDR_IN addr;
+    WSADATA wsa_data;
+    unsigned int i;
+    SOCKET s;
+
+    upnp_gateway_connection.winsock_initialized = WSAStartup( MAKEWORD( 2, 2 ), &wsa_data );
+    if ((s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP )) == -1)
+    {
+        ERR( "Failed to create socket, error %u.\n", WSAGetLastError() );
+        return FALSE;
+    }
+    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)) == SOCKET_ERROR)
+    {
+        ERR( "setsockopt(SO_RCVTIME0) failed, error %u.\n", WSAGetLastError() );
+        closesocket( s );
+        return FALSE;
+    }
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons( 1900 );
+    addr.sin_addr.S_un.S_addr = inet_addr( "239.255.255.250" );
+    if (sendto( s, upnp_search_request, strlen( upnp_search_request ), 0, (SOCKADDR *)&addr, sizeof(addr) )
+        == SOCKET_ERROR)
+    {
+        ERR( "sendto failed, error %u\n", WSAGetLastError() );
+        closesocket( s );
+        return FALSE;
+    }
+    /* Windows has a dedicated SSDP discovery service which maintains gateway device info and does
+     * not usually delay in get_StaticPortMappingCollection(). Although it may still delay depending
+     * on network connection state and always delays in IUPnPNAT_get_NATEventManager(). */
+    FIXME( "Waiting for reply from router.\n" );
+    for (i = 0; i < 2; ++i)
+    {
+        address_len = sizeof(addr);
+        len = recvfrom( s, buffer, sizeof(buffer) - 1, 0, (SOCKADDR *)&addr, &address_len );
+        if (len == -1)
+        {
+            if (WSAGetLastError() != WSAETIMEDOUT)
+            {
+                WARN( "recvfrom error %u.\n", WSAGetLastError() );
+                closesocket( s );
+                return FALSE;
+            }
+        }
+        else break;
+    }
+    closesocket( s );
+    if (i == 2)
+    {
+        TRACE( "No reply from router.\n" );
+        return FALSE;
+    }
+    TRACE( "Received reply from gateway, len %d.\n", len );
+    return TRUE;
+}
+
+static BOOL grab_gateway_connection(void)
+{
+    AcquireSRWLockExclusive( &upnp_gateway_connection_lock );
+    if (!upnp_gateway_connection.refs && !init_gateway_connection())
+    {
+        gateway_connection_cleanup();
+        ReleaseSRWLockExclusive( &upnp_gateway_connection_lock );
+        return FALSE;
+    }
+    ++upnp_gateway_connection.refs;
+    ReleaseSRWLockExclusive( &upnp_gateway_connection_lock );
+    return TRUE;
+}
+
+static void release_gateway_connection(void)
+{
+    AcquireSRWLockExclusive( &upnp_gateway_connection_lock );
+    assert( upnp_gateway_connection.refs );
+    if (!--upnp_gateway_connection.refs)
+        gateway_connection_cleanup();
+    ReleaseSRWLockExclusive( &upnp_gateway_connection_lock );
+}
+
+struct static_port_mapping_collection
+{
+    IStaticPortMappingCollection IStaticPortMappingCollection_iface;
+    LONG refs;
+};
+
+static inline struct static_port_mapping_collection *impl_from_IStaticPortMappingCollection
+                                                     ( IStaticPortMappingCollection *iface )
+{
+    return CONTAINING_RECORD(iface, struct static_port_mapping_collection, IStaticPortMappingCollection_iface);
+}
+
+static ULONG WINAPI static_ports_AddRef(
+    IStaticPortMappingCollection *iface )
+{
+    struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface );
+    return InterlockedIncrement( &ports->refs );
+}
+
+static ULONG WINAPI static_ports_Release(
+    IStaticPortMappingCollection *iface )
+{
+    struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface );
+    LONG refs = InterlockedDecrement( &ports->refs );
+    if (!refs)
+    {
+        TRACE("destroying %p\n", ports);
+        release_gateway_connection();
+        free( ports );
+    }
+    return refs;
+}
+
+static HRESULT WINAPI static_ports_QueryInterface(
+    IStaticPortMappingCollection *iface,
+    REFIID riid,
+    void **ppvObject )
+{
+    struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface );
+
+    TRACE("%p %s %p\n", ports, debugstr_guid( riid ), ppvObject );
+
+    if ( IsEqualGUID( riid, &IID_IStaticPortMappingCollection ) ||
+         IsEqualGUID( riid, &IID_IDispatch ) ||
+         IsEqualGUID( riid, &IID_IUnknown ) )
+    {
+        *ppvObject = iface;
+    }
+    else
+    {
+        FIXME("interface %s not implemented\n", debugstr_guid(riid));
+        return E_NOINTERFACE;
+    }
+    IStaticPortMappingCollection_AddRef( iface );
+    return S_OK;
+}
+
+static HRESULT WINAPI static_ports_GetTypeInfoCount(
+    IStaticPortMappingCollection *iface,
+    UINT *pctinfo )
+{
+    struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface );
+
+    TRACE("%p %p\n", ports, pctinfo);
+    *pctinfo = 1;
+    return S_OK;
+}
+
+static HRESULT WINAPI static_ports_GetTypeInfo(
+    IStaticPortMappingCollection *iface,
+    UINT iTInfo,
+    LCID lcid,
+    ITypeInfo **ppTInfo )
+{
+    struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface );
+
+    TRACE("%p %u %u %p\n", ports, iTInfo, lcid, ppTInfo);
+    return get_typeinfo( IStaticPortMappingCollection_tid, ppTInfo );
+}
+
+static HRESULT WINAPI static_ports_GetIDsOfNames(
+    IStaticPortMappingCollection *iface,
+    REFIID riid,
+    LPOLESTR *rgszNames,
+    UINT cNames,
+    LCID lcid,
+    DISPID *rgDispId )
+{
+    struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface );
+    ITypeInfo *typeinfo;
+    HRESULT hr;
+
+    TRACE("%p %s %p %u %u %p\n", ports, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
+
+    hr = get_typeinfo( IStaticPortMappingCollection_tid, &typeinfo );
+    if (SUCCEEDED(hr))
+    {
+        hr = ITypeInfo_GetIDsOfNames( typeinfo, rgszNames, cNames, rgDispId );
+        ITypeInfo_Release( typeinfo );
+    }
+    return hr;
+}
+
+static HRESULT WINAPI static_ports_Invoke(
+    IStaticPortMappingCollection *iface,
+    DISPID dispIdMember,
+    REFIID riid,
+    LCID lcid,
+    WORD wFlags,
+    DISPPARAMS *pDispParams,
+    VARIANT *pVarResult,
+    EXCEPINFO *pExcepInfo,
+    UINT *puArgErr )
+{
+    struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface );
+    ITypeInfo *typeinfo;
+    HRESULT hr;
+
+    TRACE("%p %d %s %d %d %p %p %p %p\n", ports, dispIdMember, debugstr_guid(riid),
+          lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+
+    hr = get_typeinfo( IStaticPortMappingCollection_tid, &typeinfo );
+    if (SUCCEEDED(hr))
+    {
+        hr = ITypeInfo_Invoke( typeinfo, &ports->IStaticPortMappingCollection_iface, dispIdMember,
+                               wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr );
+        ITypeInfo_Release( typeinfo );
+    }
+    return hr;
+}
+
+static HRESULT WINAPI static_ports__NewEnum(
+    IStaticPortMappingCollection *iface,
+    IUnknown **ret )
+{
+    FIXME( "iface %p, ret %p stub.\n", iface, ret );
+
+    if (ret) *ret = NULL;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI static_ports_get_Item(
+    IStaticPortMappingCollection *iface,
+    LONG port,
+    BSTR protocol,
+    IStaticPortMapping **mapping )
+{
+    FIXME( "iface %p, port %d, protocol %s stub.\n", iface, port, debugstr_w(protocol) );
+
+    if (mapping) *mapping = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI static_ports_get_Count(
+    IStaticPortMappingCollection *iface,
+    LONG *count )
+{
+    FIXME( "iface %p, count %p stub.\n", iface, count );
+
+    if (count) *count = 0;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI static_ports_Remove(
+    IStaticPortMappingCollection *iface,
+    LONG port,
+    BSTR protocol )
+{
+    FIXME( "iface %p, port %d, protocol %s stub.\n", iface, port, debugstr_w(protocol) );
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI static_ports_Add(
+    IStaticPortMappingCollection *iface,
+    LONG external,
+    BSTR protocol,
+    LONG internal,
+    BSTR client,
+    VARIANT_BOOL enabled,
+    BSTR description,
+    IStaticPortMapping **mapping )
+{
+    FIXME( "iface %p, external %d, protocol %s, internal %d, client %s, enabled %d, descritption %s, mapping %p stub.\n",
+           iface, external, debugstr_w(protocol), internal, debugstr_w(client), enabled, debugstr_w(description),
+           mapping );
+
+    if (mapping) *mapping = NULL;
+    return E_NOTIMPL;
+}
+
+static const IStaticPortMappingCollectionVtbl static_ports_vtbl =
+{
+    static_ports_QueryInterface,
+    static_ports_AddRef,
+    static_ports_Release,
+    static_ports_GetTypeInfoCount,
+    static_ports_GetTypeInfo,
+    static_ports_GetIDsOfNames,
+    static_ports_Invoke,
+    static_ports__NewEnum,
+    static_ports_get_Item,
+    static_ports_get_Count,
+    static_ports_Remove,
+    static_ports_Add,
+};
+
+static HRESULT static_port_mapping_collection_create(IStaticPortMappingCollection **object)
+{
+    struct static_port_mapping_collection *ports;
+
+    if (!object) return E_POINTER;
+    if (!grab_gateway_connection())
+    {
+        *object = NULL;
+        return S_OK;
+    }
+    if (!(ports = calloc( 1, sizeof(*ports) )))
+    {
+        release_gateway_connection();
+        return E_OUTOFMEMORY;
+    }
+    ports->refs = 1;
+    ports->IStaticPortMappingCollection_iface.lpVtbl = &static_ports_vtbl;
+    *object = &ports->IStaticPortMappingCollection_iface;
+    return S_OK;
+}
+
 typedef struct fw_port
 {
     INetFwOpenPort INetFwOpenPort_iface;
@@ -716,10 +1056,10 @@ static HRESULT WINAPI upnpnat_Invoke(IUPnPNAT *iface, DISPID dispIdMember, REFII
 static HRESULT WINAPI upnpnat_get_StaticPortMappingCollection(IUPnPNAT *iface, IStaticPortMappingCollection **collection)
 {
     upnpnat *This = impl_from_IUPnPNAT( iface );
-    FIXME("%p, %p\n", This, collection);
-    if(collection)
-        *collection = NULL;
-    return S_OK;
+
+    TRACE("%p, %p\n", This, collection);
+
+    return static_port_mapping_collection_create( collection );
 }
 
 static HRESULT WINAPI upnpnat_get_DynamicPortMappingCollection(IUPnPNAT *iface, IDynamicPortMappingCollection **collection)
diff --git a/dlls/hnetcfg/tests/policy.c b/dlls/hnetcfg/tests/policy.c
index 6abdcd7fb4e..19b6e3f6f2e 100644
--- a/dlls/hnetcfg/tests/policy.c
+++ b/dlls/hnetcfg/tests/policy.c
@@ -184,7 +184,8 @@ static void test_static_port_mapping_collection( IStaticPortMappingCollection *p
 
     refcount = get_refcount((IUnknown *)ports);
     hr = IStaticPortMappingCollection_get__NewEnum(ports, &unk);
-    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+    todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+    if (FAILED(hr)) return;
 
     hr = IUnknown_QueryInterface(unk, &IID_IEnumVARIANT, (void **)&enum_ports);
     ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
-- 
2.34.1




More information about the wine-devel mailing list