Signed-off-by: Paul Gofman pgofman@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);