2 * Web Services on Devices
5 * Copyright 2017-2018 Owen Rudge for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "wine/test.h"
29 #include "wine/heap.h"
37 #define SEND_ADDRESS_IPV4 "239.255.255.250"
38 #define SEND_ADDRESS_IPV6 "FF02::C"
39 #define SEND_PORT "3702"
41 static const char *publisherId
= "urn:uuid:3AE5617D-790F-408A-9374-359A77F924A3";
42 static const char *sequenceId
= "urn:uuid:b14de351-72fc-4453-96f9-e58b0c9faf38";
44 #define MAX_CACHED_MESSAGES 5
45 #define MAX_LISTENING_THREADS 20
47 typedef struct messageStorage
{
49 CRITICAL_SECTION criticalSection
;
50 char* messages
[MAX_CACHED_MESSAGES
];
52 HANDLE threadHandles
[MAX_LISTENING_THREADS
];
56 static LPWSTR
utf8_to_wide(const char *utf8String
)
58 int sizeNeeded
= 0, utf8StringLength
= 0, memLength
= 0;
59 LPWSTR newString
= NULL
;
61 if (utf8String
== NULL
) return NULL
;
62 utf8StringLength
= lstrlenA(utf8String
);
64 sizeNeeded
= MultiByteToWideChar(CP_UTF8
, 0, utf8String
, utf8StringLength
, NULL
, 0);
65 if (sizeNeeded
<= 0) return NULL
;
67 memLength
= sizeof(WCHAR
) * (sizeNeeded
+ 1);
68 newString
= heap_alloc_zero(memLength
);
70 MultiByteToWideChar(CP_UTF8
, 0, utf8String
, utf8StringLength
, newString
, sizeNeeded
);
74 static int join_multicast_group(SOCKET s
, struct addrinfo
*group
, struct addrinfo
*iface
)
76 int level
, optname
, optlen
;
77 struct ipv6_mreq mreqv6
;
78 struct ip_mreq mreqv4
;
81 if (group
->ai_family
== AF_INET6
)
84 optname
= IPV6_ADD_MEMBERSHIP
;
85 optval
= (char *)&mreqv6
;
86 optlen
= sizeof(mreqv6
);
88 mreqv6
.ipv6mr_multiaddr
= ((SOCKADDR_IN6
*)group
->ai_addr
)->sin6_addr
;
89 mreqv6
.ipv6mr_interface
= ((SOCKADDR_IN6
*)iface
->ai_addr
)->sin6_scope_id
;
94 optname
= IP_ADD_MEMBERSHIP
;
95 optval
= (char *)&mreqv4
;
96 optlen
= sizeof(mreqv4
);
98 mreqv4
.imr_multiaddr
.s_addr
= ((SOCKADDR_IN
*)group
->ai_addr
)->sin_addr
.s_addr
;
99 mreqv4
.imr_interface
.s_addr
= ((SOCKADDR_IN
*)iface
->ai_addr
)->sin_addr
.s_addr
;
102 return setsockopt(s
, level
, optname
, optval
, optlen
);
105 static int set_send_interface(SOCKET s
, struct addrinfo
*iface
)
107 int level
, optname
, optlen
;
110 if (iface
->ai_family
== AF_INET6
)
112 level
= IPPROTO_IPV6
;
113 optname
= IPV6_MULTICAST_IF
;
114 optval
= (char *) &((SOCKADDR_IN6
*)iface
->ai_addr
)->sin6_scope_id
;
115 optlen
= sizeof(((SOCKADDR_IN6
*)iface
->ai_addr
)->sin6_scope_id
);
120 optname
= IP_MULTICAST_IF
;
121 optval
= (char *) &((SOCKADDR_IN
*)iface
->ai_addr
)->sin_addr
.s_addr
;
122 optlen
= sizeof(((SOCKADDR_IN
*)iface
->ai_addr
)->sin_addr
.s_addr
);
125 return setsockopt(s
, level
, optname
, optval
, optlen
);
128 static struct addrinfo
*resolve_address(const char *address
, const char *port
, int family
, int type
, int protocol
)
130 struct addrinfo hints
, *result
= NULL
;
132 ZeroMemory(&hints
, sizeof(hints
));
134 hints
.ai_flags
= AI_PASSIVE
;
135 hints
.ai_family
= family
;
136 hints
.ai_socktype
= type
;
137 hints
.ai_protocol
= protocol
;
139 return getaddrinfo(address
, port
, &hints
, &result
) == 0 ? result
: NULL
;
142 typedef struct listenerThreadParams
144 messageStorage
*msgStorage
;
145 SOCKET listeningSocket
;
146 } listenerThreadParams
;
148 #define RECEIVE_BUFFER_SIZE 65536
150 static DWORD WINAPI
listening_thread(LPVOID lpParam
)
152 listenerThreadParams
*parameter
= (listenerThreadParams
*)lpParam
;
153 messageStorage
*msgStorage
= parameter
->msgStorage
;
157 buffer
= heap_alloc(RECEIVE_BUFFER_SIZE
);
159 while (parameter
->msgStorage
->running
)
161 ZeroMemory(buffer
, RECEIVE_BUFFER_SIZE
);
162 bytesReceived
= recv(parameter
->listeningSocket
, buffer
, RECEIVE_BUFFER_SIZE
, 0);
164 if (bytesReceived
== SOCKET_ERROR
)
166 if (WSAGetLastError() != WSAETIMEDOUT
)
171 EnterCriticalSection(&msgStorage
->criticalSection
);
173 if (msgStorage
->messageCount
< MAX_CACHED_MESSAGES
)
175 msgStorage
->messages
[msgStorage
->messageCount
] = heap_alloc(bytesReceived
);
177 if (msgStorage
->messages
[msgStorage
->messageCount
] != NULL
)
179 memcpy(msgStorage
->messages
[msgStorage
->messageCount
], buffer
, bytesReceived
);
180 msgStorage
->messageCount
++;
184 LeaveCriticalSection(&msgStorage
->criticalSection
);
186 if (msgStorage
->messageCount
>= MAX_CACHED_MESSAGES
)
188 /* Stop all threads */
189 msgStorage
->running
= FALSE
;
195 closesocket(parameter
->listeningSocket
);
198 heap_free(parameter
);
203 static void start_listening(messageStorage
*msgStorage
, const char *multicastAddress
, const char *bindAddress
)
205 struct addrinfo
*multicastAddr
= NULL
, *bindAddr
= NULL
, *interfaceAddr
= NULL
;
206 listenerThreadParams
*parameter
= NULL
;
207 const DWORD receiveTimeout
= 5000;
208 const UINT reuseAddr
= 1;
212 /* Resolve the multicast address */
213 multicastAddr
= resolve_address(multicastAddress
, SEND_PORT
, AF_UNSPEC
, SOCK_DGRAM
, IPPROTO_UDP
);
214 if (multicastAddr
== NULL
) goto cleanup
;
216 /* Resolve the binding address */
217 bindAddr
= resolve_address(bindAddress
, SEND_PORT
, multicastAddr
->ai_family
, multicastAddr
->ai_socktype
, multicastAddr
->ai_protocol
);
218 if (bindAddr
== NULL
) goto cleanup
;
220 /* Resolve the multicast interface */
221 interfaceAddr
= resolve_address(bindAddress
, "0", multicastAddr
->ai_family
, multicastAddr
->ai_socktype
, multicastAddr
->ai_protocol
);
222 if (interfaceAddr
== NULL
) goto cleanup
;
224 /* Create the socket */
225 s
= socket(multicastAddr
->ai_family
, multicastAddr
->ai_socktype
, multicastAddr
->ai_protocol
);
226 if (s
== INVALID_SOCKET
) goto cleanup
;
228 /* Ensure the socket can be reused */
229 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, (const char *)&reuseAddr
, sizeof(reuseAddr
)) == SOCKET_ERROR
) goto cleanup
;
231 /* Bind the socket to the local interface so we can receive data */
232 if (bind(s
, bindAddr
->ai_addr
, bindAddr
->ai_addrlen
) == SOCKET_ERROR
) goto cleanup
;
234 /* Join the multicast group */
235 if (join_multicast_group(s
, multicastAddr
, interfaceAddr
) == SOCKET_ERROR
) goto cleanup
;
237 /* Set the outgoing interface */
238 if (set_send_interface(s
, interfaceAddr
) == SOCKET_ERROR
) goto cleanup
;
240 /* For IPv6, ensure the scope ID is zero */
241 if (multicastAddr
->ai_family
== AF_INET6
)
242 ((SOCKADDR_IN6
*)multicastAddr
->ai_addr
)->sin6_scope_id
= 0;
244 /* Set a 5-second receive timeout */
245 if (setsockopt(s
, SOL_SOCKET
, SO_RCVTIMEO
, (const char *)&receiveTimeout
, sizeof(receiveTimeout
)) == SOCKET_ERROR
) goto cleanup
;
247 /* Allocate memory for thread parameters */
248 parameter
= heap_alloc(sizeof(listenerThreadParams
));
250 parameter
->msgStorage
= msgStorage
;
251 parameter
->listeningSocket
= s
;
253 hThread
= CreateThread(NULL
, 0, listening_thread
, parameter
, 0, NULL
);
254 if (hThread
== NULL
) goto cleanup
;
256 msgStorage
->threadHandles
[msgStorage
->numThreadHandles
] = hThread
;
257 msgStorage
->numThreadHandles
++;
259 goto cleanup_addresses
;
263 if (parameter
!= NULL
) heap_free(parameter
);
266 freeaddrinfo(multicastAddr
);
267 freeaddrinfo(bindAddr
);
268 freeaddrinfo(interfaceAddr
);
271 static BOOL
start_listening_on_all_addresses(messageStorage
*msgStorage
, ULONG family
)
273 IP_ADAPTER_ADDRESSES
*adapterAddresses
= NULL
, *adapterAddress
;
274 ULONG bufferSize
= 0;
281 retVal
= GetAdaptersAddresses(family
, 0, NULL
, NULL
, &bufferSize
); /* family should be AF_INET or AF_INET6 */
282 if (retVal
!= ERROR_BUFFER_OVERFLOW
) goto cleanup
;
284 /* Get size of buffer for adapters */
285 adapterAddresses
= (IP_ADAPTER_ADDRESSES
*)heap_alloc(bufferSize
);
286 if (adapterAddresses
== NULL
) goto cleanup
;
288 /* Get list of adapters */
289 retVal
= GetAdaptersAddresses(family
, 0, NULL
, adapterAddresses
, &bufferSize
);
290 if (retVal
!= ERROR_SUCCESS
) goto cleanup
;
292 for (adapterAddress
= adapterAddresses
; adapterAddress
!= NULL
; adapterAddress
= adapterAddress
->Next
)
294 if (msgStorage
->numThreadHandles
>= MAX_LISTENING_THREADS
)
300 if (adapterAddress
->FirstUnicastAddress
== NULL
) continue;
302 sockaddr
= adapterAddress
->FirstUnicastAddress
->Address
.lpSockaddr
;
303 addressLength
= sizeof(address
);
304 WSAAddressToStringA(sockaddr
, adapterAddress
->FirstUnicastAddress
->Address
.iSockaddrLength
, NULL
, address
, &addressLength
);
306 start_listening(msgStorage
, adapterAddress
->FirstUnicastAddress
->Address
.lpSockaddr
->sa_family
== AF_INET
? SEND_ADDRESS_IPV4
: SEND_ADDRESS_IPV6
, address
);
312 if (adapterAddresses
!= NULL
) heap_free(adapterAddresses
);
316 typedef struct IWSDiscoveryPublisherNotifyImpl
{
317 IWSDiscoveryPublisherNotify IWSDiscoveryPublisherNotify_iface
;
319 } IWSDiscoveryPublisherNotifyImpl
;
321 static inline IWSDiscoveryPublisherNotifyImpl
*impl_from_IWSDiscoveryPublisherNotify(IWSDiscoveryPublisherNotify
*iface
)
323 return CONTAINING_RECORD(iface
, IWSDiscoveryPublisherNotifyImpl
, IWSDiscoveryPublisherNotify_iface
);
326 static HRESULT WINAPI
IWSDiscoveryPublisherNotifyImpl_QueryInterface(IWSDiscoveryPublisherNotify
*iface
, REFIID riid
, void **ppv
)
328 IWSDiscoveryPublisherNotifyImpl
*This
= impl_from_IWSDiscoveryPublisherNotify(iface
);
337 if (IsEqualIID(riid
, &IID_IUnknown
) ||
338 IsEqualIID(riid
, &IID_IWSDiscoveryPublisherNotify
))
340 *ppv
= &This
->IWSDiscoveryPublisherNotify_iface
;
344 return E_NOINTERFACE
;
347 IUnknown_AddRef((IUnknown
*)*ppv
);
351 static ULONG WINAPI
IWSDiscoveryPublisherNotifyImpl_AddRef(IWSDiscoveryPublisherNotify
*iface
)
353 IWSDiscoveryPublisherNotifyImpl
*This
= impl_from_IWSDiscoveryPublisherNotify(iface
);
354 ULONG ref
= InterlockedIncrement(&This
->ref
);
356 trace("IWSDiscoveryPublisherNotifyImpl_AddRef called (%p, ref = %d)\n", This
, ref
);
360 static ULONG WINAPI
IWSDiscoveryPublisherNotifyImpl_Release(IWSDiscoveryPublisherNotify
*iface
)
362 IWSDiscoveryPublisherNotifyImpl
*This
= impl_from_IWSDiscoveryPublisherNotify(iface
);
363 ULONG ref
= InterlockedDecrement(&This
->ref
);
365 trace("IWSDiscoveryPublisherNotifyImpl_Release called (%p, ref = %d)\n", This
, ref
);
369 HeapFree(GetProcessHeap(), 0, This
);
375 static HRESULT WINAPI
IWSDiscoveryPublisherNotifyImpl_ProbeHandler(IWSDiscoveryPublisherNotify
*This
, const WSD_SOAP_MESSAGE
*pSoap
, IWSDMessageParameters
*pMessageParameters
)
377 trace("IWSDiscoveryPublisherNotifyImpl_ProbeHandler called (%p, %p, %p)\n", This
, pSoap
, pMessageParameters
);
381 static HRESULT WINAPI
IWSDiscoveryPublisherNotifyImpl_ResolveHandler(IWSDiscoveryPublisherNotify
*This
, const WSD_SOAP_MESSAGE
*pSoap
, IWSDMessageParameters
*pMessageParameters
)
383 trace("IWSDiscoveryPublisherNotifyImpl_ResolveHandler called (%p, %p, %p)\n", This
, pSoap
, pMessageParameters
);
387 static const IWSDiscoveryPublisherNotifyVtbl publisherNotify_vtbl
=
389 IWSDiscoveryPublisherNotifyImpl_QueryInterface
,
390 IWSDiscoveryPublisherNotifyImpl_AddRef
,
391 IWSDiscoveryPublisherNotifyImpl_Release
,
392 IWSDiscoveryPublisherNotifyImpl_ProbeHandler
,
393 IWSDiscoveryPublisherNotifyImpl_ResolveHandler
396 static BOOL
create_discovery_publisher_notify(IWSDiscoveryPublisherNotify
**publisherNotify
)
398 IWSDiscoveryPublisherNotifyImpl
*obj
;
400 *publisherNotify
= NULL
;
402 obj
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*obj
));
406 trace("Out of memory creating IWSDiscoveryPublisherNotify\n");
410 obj
->IWSDiscoveryPublisherNotify_iface
.lpVtbl
= &publisherNotify_vtbl
;
413 *publisherNotify
= &obj
->IWSDiscoveryPublisherNotify_iface
;
418 static void CreateDiscoveryPublisher_tests(void)
420 IWSDiscoveryPublisher
*publisher
= NULL
;
421 IWSDiscoveryPublisher
*publisher2
;
426 rc
= WSDCreateDiscoveryPublisher(NULL
, NULL
);
427 ok((rc
== E_POINTER
) || (rc
== E_INVALIDARG
), "WSDCreateDiscoveryPublisher(NULL, NULL) failed: %08x\n", rc
);
429 rc
= WSDCreateDiscoveryPublisher(NULL
, &publisher
);
430 ok(rc
== S_OK
, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc
);
431 ok(publisher
!= NULL
, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
433 /* Try to query for objects */
434 rc
= IWSDiscoveryPublisher_QueryInterface(publisher
, &IID_IUnknown
, (LPVOID
*)&unknown
);
435 ok(rc
== S_OK
,"IWSDiscoveryPublisher_QueryInterface(IID_IUnknown) failed: %08x\n", rc
);
438 IUnknown_Release(unknown
);
440 rc
= IWSDiscoveryPublisher_QueryInterface(publisher
, &IID_IWSDiscoveryPublisher
, (LPVOID
*)&publisher2
);
441 ok(rc
== S_OK
,"IWSDiscoveryPublisher_QueryInterface(IID_IWSDiscoveryPublisher) failed: %08x\n", rc
);
444 IWSDiscoveryPublisher_Release(publisher2
);
446 ref
= IWSDiscoveryPublisher_Release(publisher
);
447 ok(ref
== 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref
);
450 static void CreateDiscoveryPublisher_XMLContext_tests(void)
452 IWSDiscoveryPublisher
*publisher
= NULL
;
453 IWSDXMLContext
*xmlContext
, *returnedContext
;
457 /* Test creating an XML context and supplying it to WSDCreateDiscoveryPublisher */
458 rc
= WSDXMLCreateContext(&xmlContext
);
459 ok(rc
== S_OK
, "WSDXMLCreateContext failed: %08x\n", rc
);
461 rc
= WSDCreateDiscoveryPublisher(xmlContext
, &publisher
);
462 ok(rc
== S_OK
, "WSDCreateDiscoveryPublisher(xmlContext, &publisher) failed: %08x\n", rc
);
463 ok(publisher
!= NULL
, "WSDCreateDiscoveryPublisher(xmlContext, &publisher) failed: publisher == NULL\n");
465 rc
= IWSDiscoveryPublisher_GetXMLContext(publisher
, NULL
);
466 ok(rc
== E_INVALIDARG
, "GetXMLContext returned unexpected value with NULL argument: %08x\n", rc
);
468 rc
= IWSDiscoveryPublisher_GetXMLContext(publisher
, &returnedContext
);
469 ok(rc
== S_OK
, "GetXMLContext failed: %08x\n", rc
);
471 ok(xmlContext
== returnedContext
, "GetXMLContext returned unexpected value: returnedContext == %p\n", returnedContext
);
473 ref
= IWSDXMLContext_Release(returnedContext
);
474 ok(ref
== 2, "IWSDXMLContext_Release() has %d references, should have 2\n", ref
);
476 ref
= IWSDiscoveryPublisher_Release(publisher
);
477 ok(ref
== 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref
);
479 ref
= IWSDXMLContext_Release(returnedContext
);
480 ok(ref
== 0, "IWSDXMLContext_Release() has %d references, should have 0\n", ref
);
482 /* Test using a default XML context */
484 returnedContext
= NULL
;
486 rc
= WSDCreateDiscoveryPublisher(NULL
, &publisher
);
487 ok(rc
== S_OK
, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc
);
488 ok(publisher
!= NULL
, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
490 rc
= IWSDiscoveryPublisher_GetXMLContext(publisher
, &returnedContext
);
491 ok(rc
== S_OK
, "GetXMLContext failed: %08x\n", rc
);
493 ref
= IWSDXMLContext_Release(returnedContext
);
494 ok(ref
== 1, "IWSDXMLContext_Release() has %d references, should have 1\n", ref
);
496 ref
= IWSDiscoveryPublisher_Release(publisher
);
497 ok(ref
== 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref
);
500 static void Publish_tests(void)
502 IWSDiscoveryPublisher
*publisher
= NULL
;
503 IWSDiscoveryPublisherNotify
*sink1
= NULL
, *sink2
= NULL
;
504 IWSDiscoveryPublisherNotifyImpl
*sink1Impl
= NULL
, *sink2Impl
= NULL
;
505 char endpointReferenceString
[MAX_PATH
], app_sequence_string
[MAX_PATH
];
506 LPWSTR publisherIdW
= NULL
, sequenceIdW
= NULL
;
507 messageStorage
*msgStorage
;
509 BOOL messageOK
, hello_message_seen
= FALSE
, endpoint_reference_seen
= FALSE
, app_sequence_seen
= FALSE
;
510 BOOL metadata_version_seen
= FALSE
;
516 rc
= WSDCreateDiscoveryPublisher(NULL
, &publisher
);
517 ok(rc
== S_OK
, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc
);
518 ok(publisher
!= NULL
, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
520 /* Test SetAddressFamily */
521 rc
= IWSDiscoveryPublisher_SetAddressFamily(publisher
, 12345);
522 ok(rc
== E_INVALIDARG
, "IWSDiscoveryPublisher_SetAddressFamily(12345) returned unexpected result: %08x\n", rc
);
524 rc
= IWSDiscoveryPublisher_SetAddressFamily(publisher
, WSDAPI_ADDRESSFAMILY_IPV4
);
525 ok(rc
== S_OK
, "IWSDiscoveryPublisher_SetAddressFamily(WSDAPI_ADDRESSFAMILY_IPV4) failed: %08x\n", rc
);
527 /* Try to update the address family after already setting it */
528 rc
= IWSDiscoveryPublisher_SetAddressFamily(publisher
, WSDAPI_ADDRESSFAMILY_IPV6
);
529 ok(rc
== STG_E_INVALIDFUNCTION
, "IWSDiscoveryPublisher_SetAddressFamily(WSDAPI_ADDRESSFAMILY_IPV6) returned unexpected result: %08x\n", rc
);
531 /* Create notification sinks */
532 ok(create_discovery_publisher_notify(&sink1
) == TRUE
, "create_discovery_publisher_notify failed\n");
533 ok(create_discovery_publisher_notify(&sink2
) == TRUE
, "create_discovery_publisher_notify failed\n");
535 /* Get underlying implementation so we can check the ref count */
536 sink1Impl
= impl_from_IWSDiscoveryPublisherNotify(sink1
);
537 sink2Impl
= impl_from_IWSDiscoveryPublisherNotify(sink2
);
539 /* Attempt to unregister sink before registering it */
540 rc
= IWSDiscoveryPublisher_UnRegisterNotificationSink(publisher
, sink1
);
541 ok(rc
== E_FAIL
, "IWSDiscoveryPublisher_UnRegisterNotificationSink returned unexpected result: %08x\n", rc
);
543 /* Register notification sinks */
544 rc
= IWSDiscoveryPublisher_RegisterNotificationSink(publisher
, sink1
);
545 ok(rc
== S_OK
, "IWSDiscoveryPublisher_RegisterNotificationSink failed: %08x\n", rc
);
546 ok(sink1Impl
->ref
== 2, "Ref count for sink 1 is not as expected: %d\n", sink1Impl
->ref
);
548 rc
= IWSDiscoveryPublisher_RegisterNotificationSink(publisher
, sink2
);
549 ok(rc
== S_OK
, "IWSDiscoveryPublisher_RegisterNotificationSink failed: %08x\n", rc
);
550 ok(sink2Impl
->ref
== 2, "Ref count for sink 2 is not as expected: %d\n", sink2Impl
->ref
);
552 /* Unregister the first sink */
553 rc
= IWSDiscoveryPublisher_UnRegisterNotificationSink(publisher
, sink1
);
554 ok(rc
== S_OK
, "IWSDiscoveryPublisher_UnRegisterNotificationSink failed: %08x\n", rc
);
555 ok(sink1Impl
->ref
== 1, "Ref count for sink 1 is not as expected: %d\n", sink1Impl
->ref
);
557 /* Set up network listener */
558 publisherIdW
= utf8_to_wide(publisherId
);
559 if (publisherIdW
== NULL
) goto after_publish_test
;
561 sequenceIdW
= utf8_to_wide(sequenceId
);
562 if (sequenceIdW
== NULL
) goto after_publish_test
;
564 msgStorage
= heap_alloc_zero(sizeof(messageStorage
));
565 if (msgStorage
== NULL
) goto after_publish_test
;
567 msgStorage
->running
= TRUE
;
568 InitializeCriticalSection(&msgStorage
->criticalSection
);
570 ret
= WSAStartup(MAKEWORD(2, 2), &wsaData
);
571 ok(ret
== 0, "WSAStartup failed (ret = %d)\n", ret
);
573 ret
= start_listening_on_all_addresses(msgStorage
, AF_INET
);
574 ok(ret
== TRUE
, "Unable to listen on IPv4 addresses (ret == %d)\n", ret
);
576 /* Publish the service */
577 rc
= IWSDiscoveryPublisher_Publish(publisher
, publisherIdW
, 1, 1, 1, sequenceIdW
, NULL
, NULL
, NULL
);
578 ok(rc
== S_OK
, "Publish failed: %08x\n", rc
);
580 /* Wait up to 2 seconds for messages to be received */
581 if (WaitForMultipleObjects(msgStorage
->numThreadHandles
, msgStorage
->threadHandles
, TRUE
, 2000) == WAIT_TIMEOUT
)
583 /* Wait up to 1 more second for threads to terminate */
584 msgStorage
->running
= FALSE
;
585 WaitForMultipleObjects(msgStorage
->numThreadHandles
, msgStorage
->threadHandles
, TRUE
, 1000);
588 DeleteCriticalSection(&msgStorage
->criticalSection
);
590 /* Verify we've received a message */
591 ok(msgStorage
->messageCount
>= 1, "No messages received\n");
593 sprintf(endpointReferenceString
, "<wsa:EndpointReference><wsa:Address>%s</wsa:Address></wsa:EndpointReference>", publisherId
);
594 sprintf(app_sequence_string
, "<wsd:AppSequence InstanceId=\"1\" SequenceId=\"%s\" MessageNumber=\"1\"></wsd:AppSequence>",
599 /* Check we're received the correct message */
600 for (i
= 0; i
< msgStorage
->messageCount
; i
++)
602 msg
= msgStorage
->messages
[i
];
605 hello_message_seen
= (strstr(msg
, "<wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Hello</wsa:Action>") != NULL
);
606 endpoint_reference_seen
= (strstr(msg
, endpointReferenceString
) != NULL
);
607 app_sequence_seen
= (strstr(msg
, app_sequence_string
) != NULL
);
608 metadata_version_seen
= (strstr(msg
, "<wsd:MetadataVersion>1</wsd:MetadataVersion>") != NULL
);
609 messageOK
= hello_message_seen
&& endpoint_reference_seen
&& app_sequence_seen
&& metadata_version_seen
;
611 if (messageOK
) break;
614 for (i
= 0; i
< msgStorage
->messageCount
; i
++)
616 heap_free(msgStorage
->messages
[i
]);
619 heap_free(msgStorage
);
621 ok(hello_message_seen
== TRUE
, "Hello message not received\n");
622 todo_wine
ok(endpoint_reference_seen
== TRUE
, "EndpointReference not received\n");
623 ok(app_sequence_seen
== TRUE
, "AppSequence not received\n");
624 todo_wine
ok(metadata_version_seen
== TRUE
, "MetadataVersion not received\n");
625 todo_wine
ok(messageOK
== TRUE
, "Hello message metadata not received\n");
629 if (publisherIdW
!= NULL
) heap_free(publisherIdW
);
630 if (sequenceIdW
!= NULL
) heap_free(sequenceIdW
);
632 ref
= IWSDiscoveryPublisher_Release(publisher
);
633 ok(ref
== 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref
);
635 /* Check that the sinks have been released by the publisher */
636 ok(sink1Impl
->ref
== 1, "Ref count for sink 1 is not as expected: %d\n", sink1Impl
->ref
);
637 ok(sink2Impl
->ref
== 1, "Ref count for sink 2 is not as expected: %d\n", sink2Impl
->ref
);
639 /* Release the sinks */
640 IWSDiscoveryPublisherNotify_Release(sink1
);
641 IWSDiscoveryPublisherNotify_Release(sink2
);
652 static BOOL
is_process_elevated(void)
655 if (OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY
, &token
))
657 TOKEN_ELEVATION_TYPE type
;
661 ret
= GetTokenInformation( token
, TokenElevationType
, &type
, sizeof(type
), &size
);
662 CloseHandle( token
);
663 return (ret
&& type
== TokenElevationTypeFull
);
668 static BOOL
is_firewall_enabled(void)
671 INetFwMgr
*mgr
= NULL
;
672 INetFwPolicy
*policy
= NULL
;
673 INetFwProfile
*profile
= NULL
;
674 VARIANT_BOOL enabled
= VARIANT_FALSE
;
676 init
= CoInitializeEx( 0, COINIT_APARTMENTTHREADED
);
678 hr
= CoCreateInstance( &CLSID_NetFwMgr
, NULL
, CLSCTX_INPROC_SERVER
, &IID_INetFwMgr
,
680 ok( hr
== S_OK
, "got %08x\n", hr
);
681 if (hr
!= S_OK
) goto done
;
683 hr
= INetFwMgr_get_LocalPolicy( mgr
, &policy
);
684 ok( hr
== S_OK
, "got %08x\n", hr
);
685 if (hr
!= S_OK
) goto done
;
687 hr
= INetFwPolicy_get_CurrentProfile( policy
, &profile
);
688 if (hr
!= S_OK
) goto done
;
690 hr
= INetFwProfile_get_FirewallEnabled( profile
, &enabled
);
691 ok( hr
== S_OK
, "got %08x\n", hr
);
694 if (policy
) INetFwPolicy_Release( policy
);
695 if (profile
) INetFwProfile_Release( profile
);
696 if (mgr
) INetFwMgr_Release( mgr
);
697 if (SUCCEEDED( init
)) CoUninitialize();
698 return (enabled
== VARIANT_TRUE
);
701 static HRESULT
set_firewall( enum firewall_op op
)
703 static const WCHAR testW
[] = {'w','s','d','a','p','i','_','t','e','s','t',0};
705 INetFwMgr
*mgr
= NULL
;
706 INetFwPolicy
*policy
= NULL
;
707 INetFwProfile
*profile
= NULL
;
708 INetFwAuthorizedApplication
*app
= NULL
;
709 INetFwAuthorizedApplications
*apps
= NULL
;
710 BSTR name
, image
= SysAllocStringLen( NULL
, MAX_PATH
);
712 if (!GetModuleFileNameW( NULL
, image
, MAX_PATH
))
714 SysFreeString( image
);
717 init
= CoInitializeEx( 0, COINIT_APARTMENTTHREADED
);
719 hr
= CoCreateInstance( &CLSID_NetFwMgr
, NULL
, CLSCTX_INPROC_SERVER
, &IID_INetFwMgr
,
721 ok( hr
== S_OK
, "got %08x\n", hr
);
722 if (hr
!= S_OK
) goto done
;
724 hr
= INetFwMgr_get_LocalPolicy( mgr
, &policy
);
725 ok( hr
== S_OK
, "got %08x\n", hr
);
726 if (hr
!= S_OK
) goto done
;
728 hr
= INetFwPolicy_get_CurrentProfile( policy
, &profile
);
729 if (hr
!= S_OK
) goto done
;
731 hr
= INetFwProfile_get_AuthorizedApplications( profile
, &apps
);
732 ok( hr
== S_OK
, "got %08x\n", hr
);
733 if (hr
!= S_OK
) goto done
;
735 hr
= CoCreateInstance( &CLSID_NetFwAuthorizedApplication
, NULL
, CLSCTX_INPROC_SERVER
,
736 &IID_INetFwAuthorizedApplication
, (void **)&app
);
737 ok( hr
== S_OK
, "got %08x\n", hr
);
738 if (hr
!= S_OK
) goto done
;
740 hr
= INetFwAuthorizedApplication_put_ProcessImageFileName( app
, image
);
741 if (hr
!= S_OK
) goto done
;
743 name
= SysAllocString( testW
);
744 hr
= INetFwAuthorizedApplication_put_Name( app
, name
);
745 SysFreeString( name
);
746 ok( hr
== S_OK
, "got %08x\n", hr
);
747 if (hr
!= S_OK
) goto done
;
750 hr
= INetFwAuthorizedApplications_Add( apps
, app
);
751 else if (op
== APP_REMOVE
)
752 hr
= INetFwAuthorizedApplications_Remove( apps
, image
);
757 if (app
) INetFwAuthorizedApplication_Release( app
);
758 if (apps
) INetFwAuthorizedApplications_Release( apps
);
759 if (policy
) INetFwPolicy_Release( policy
);
760 if (profile
) INetFwProfile_Release( profile
);
761 if (mgr
) INetFwMgr_Release( mgr
);
762 if (SUCCEEDED( init
)) CoUninitialize();
763 SysFreeString( image
);
767 START_TEST(discovery
)
769 BOOL firewall_enabled
= is_firewall_enabled();
772 if (firewall_enabled
)
774 if (!is_process_elevated())
776 skip("no privileges, skipping tests to avoid firewall dialog\n");
779 if ((hr
= set_firewall(APP_ADD
)) != S_OK
)
781 skip("can't authorize app in firewall %08x\n", hr
);
788 CreateDiscoveryPublisher_tests();
789 CreateDiscoveryPublisher_XMLContext_tests();
793 if (firewall_enabled
) set_firewall(APP_REMOVE
);