From f54383b32c71d0d9ffd712a73334415a20def10f Mon Sep 17 00:00:00 2001 From: Dave Thaler Date: Fri, 21 Oct 2016 14:34:13 -0700 Subject: [PATCH] Add store app workaround for Windows network monitor Also fix some typos in run.bat Change-Id: I0060b833a5c984bfb0555168927db845bee76c3e Signed-off-by: Dave Thaler Reviewed-on: https://gerrit.iotivity.org/gerrit/13635 Tested-by: jenkins-iotivity Reviewed-by: Soemin Tjong Reviewed-by: Dan Mihai Reviewed-by: David Antler --- .../src/ip_adapter/windows/caipnwmonitor.c | 191 +++++++++++++++++++++ run.bat | 10 +- 2 files changed, 197 insertions(+), 4 deletions(-) diff --git a/resource/csdk/connectivity/src/ip_adapter/windows/caipnwmonitor.c b/resource/csdk/connectivity/src/ip_adapter/windows/caipnwmonitor.c index 6350025..2e401e2 100644 --- a/resource/csdk/connectivity/src/ip_adapter/windows/caipnwmonitor.c +++ b/resource/csdk/connectivity/src/ip_adapter/windows/caipnwmonitor.c @@ -39,6 +39,15 @@ #define TAG "IP_MONITOR" +// When this is defined, a socket will be used to get address change events. The +// SIO_ADDRESS_LIST_CHANGE socket option is accessible on all versions of Windows +// and is available to both desktop and store apps, but it is more complex and hence +// more appropriate for use by applications using SIO_ADDRESS_LIST_QUERY to get addresses +// from Winsock rather than GetAdaptersAddresses() which we need to use to get additional +// information. We expect NotifyUnicastIpAddressChange will be available to store apps +// at some point in the future and this workaround can go away at that point. +#define USE_SOCKET_ADDRESS_CHANGE_EVENT + /** * Mutex for synchronizing access to cached address information. */ @@ -229,6 +238,177 @@ static void CALLBACK IpAddressChangeCallback(void *context, oc_mutex_unlock(g_CAIPNetworkMonitorMutex); } +#ifdef USE_SOCKET_ADDRESS_CHANGE_EVENT +static HANDLE g_CAIPNetworkMonitorShutdownEvent = NULL; + +void UnregisterForIpAddressChange() +{ + if (g_CAIPNetworkMonitorShutdownEvent != NULL) + { + // Cancel the worker thread. + if (SetEvent(g_CAIPNetworkMonitorShutdownEvent)) + { + WaitForSingleObject(g_CAIPNetworkMonitorChangeNotificationHandle, INFINITE); + } + + CloseHandle(g_CAIPNetworkMonitorShutdownEvent); + g_CAIPNetworkMonitorShutdownEvent = NULL; + } + if (g_CAIPNetworkMonitorChangeNotificationHandle != NULL) + { + CloseHandle(g_CAIPNetworkMonitorChangeNotificationHandle); + g_CAIPNetworkMonitorChangeNotificationHandle = NULL; + } +} + +DWORD WINAPI IpNetworkMonitorWorker(PVOID context) +{ + OVERLAPPED overlapped = {0}; + + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData = {.wVersion = 0}; + int err = WSAStartup(wVersionRequested, &wsaData); + if (err != NO_ERROR) + { + return err; + } + + SOCKET nwmSocket = WSASocket( + AF_INET6, + SOCK_DGRAM, + 0, // Default proto. + NULL, // No other protocol info. + 0, // Ignore group. + WSA_FLAG_OVERLAPPED); + if (INVALID_SOCKET == nwmSocket) + { + err = WSAGetLastError(); + goto done; + } + + // Put socket into dual IPv4/IPv6 mode. + BOOL ipv6Only = FALSE; + if (SOCKET_ERROR == setsockopt(nwmSocket, + IPPROTO_IPV6, + IPV6_V6ONLY, + (char*)&ipv6Only, + sizeof(ipv6Only))) + { + err = WSAGetLastError(); + goto done; + } + + overlapped.hEvent = CreateEvent( + NULL, // No security descriptor. + TRUE, // Manual reset event. + FALSE, // Start not signaled. + NULL); // No name. + if (NULL == overlapped.hEvent) + { + err = GetLastError(); + goto done; + } + + WSAEVENT eventList[2] = { overlapped.hEvent, + g_CAIPNetworkMonitorShutdownEvent, + }; + DWORD bytesReturned = 0; + for (;;) + { + if (SOCKET_ERROR == WSAIoctl(nwmSocket, + SIO_ADDRESS_LIST_CHANGE, + NULL, // No input buffer. + 0, + NULL, // No outupt buffer. + 0, + &bytesReturned, + &overlapped, + NULL)) // No completion routine. + { + err = WSAGetLastError(); + if (err != ERROR_IO_PENDING) + { + break; + } + else + { + // Wait for an address change or a request to cancel the thread. + DWORD waitStatus = WSAWaitForMultipleEvents(_countof(eventList), + eventList, + FALSE, // Wait for any one to fire. + WSA_INFINITE, + FALSE); // No I/O completion routines. + + if (waitStatus != WSA_WAIT_EVENT_0) + { + // The cancel event was signaled. There is no need to call CancelIo + // here, because we will close the socket handle below, causing any + // pending I/O to be canceled then. + assert(waitStatus == WSA_WAIT_EVENT_0 + 1); + break; + } + + WSAResetEvent(overlapped.hEvent); + } + } + + // We have a change to process. The address change callback ignores + // the parameters, so we just pass default values. + IpAddressChangeCallback(context, NULL, MibInitialNotification); + } + +done: + if (nwmSocket != INVALID_SOCKET) + { + closesocket(nwmSocket); + nwmSocket = INVALID_SOCKET; + } + if (overlapped.hEvent != NULL) + { + CloseHandle(overlapped.hEvent); + overlapped.hEvent = NULL; + } + WSACleanup(); + return err; +} + +BOOL RegisterForIpAddressChange() +{ + assert(g_CAIPNetworkMonitorChangeNotificationHandle == NULL); + + g_CAIPNetworkMonitorShutdownEvent = CreateEvent( + NULL, // No security descriptor. + TRUE, // Manual reset event. + FALSE, // Start not signaled. + NULL); // No name. + if (g_CAIPNetworkMonitorShutdownEvent == NULL) + { + return false; + } + + g_CAIPNetworkMonitorChangeNotificationHandle = CreateThread( + NULL, // Default security attributes. + 0, // Default stack size. + IpNetworkMonitorWorker, + NULL, // No context. + 0, // Run immediately. + NULL); // We don't need the thread id. + + if (g_CAIPNetworkMonitorChangeNotificationHandle == NULL) + { + CloseHandle(g_CAIPNetworkMonitorShutdownEvent); + g_CAIPNetworkMonitorShutdownEvent = NULL; + return false; + } + + // Signal the callback to query the initial state. The address change callback ignores + // the parameters, so we just pass default values. + IpAddressChangeCallback(NULL, NULL, MibInitialNotification); + + return true; +} +#endif + /** * Start network monitor. * @@ -253,6 +433,12 @@ CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback, if (g_CAIPNetworkMonitorChangeNotificationHandle == NULL) { +#ifdef USE_SOCKET_ADDRESS_CHANGE_EVENT + if (!RegisterForIpAddressChange()) + { + return CA_STATUS_FAILED; + } +#else int err = NotifyUnicastIpAddressChange(AF_UNSPEC, IpAddressChangeCallback, NULL, true, &g_CAIPNetworkMonitorChangeNotificationHandle); @@ -260,6 +446,7 @@ CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback, { return CA_STATUS_FAILED; } +#endif } return CA_STATUS_OK; } @@ -274,9 +461,13 @@ CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter) { if (g_CAIPNetworkMonitorChangeNotificationHandle != NULL) { +#ifdef USE_SOCKET_ADDRESS_CHANGE_EVENT + UnregisterForIpAddressChange(); +#else int err = CancelMibChangeNotify2(g_CAIPNetworkMonitorChangeNotificationHandle); assert(err == NO_ERROR); g_CAIPNetworkMonitorChangeNotificationHandle = NULL; +#endif } CAIPDestroyNetworkMonitorList(); diff --git a/run.bat b/run.bat index 53e2f79..1c7b1c9 100644 --- a/run.bat +++ b/run.bat @@ -97,7 +97,7 @@ if "!RUN_ARG!"=="server" ( ) else if "!RUN_ARG!"=="clienthq" ( cd %BUILD_DIR%\resource\examples %DEBUG% simpleclientHQ.exe -)else if "!RUN_ARG!"=="mediaclient" ( +) else if "!RUN_ARG!"=="mediaclient" ( cd %BUILD_DIR%\resource\examples %DEBUG% mediaclient.exe ) else if "!RUN_ARG!"=="mediaserver" ( @@ -140,6 +140,7 @@ if "!RUN_ARG!"=="server" ( echo WITH_RD=%WITH_RD% echo ROUTING=%ROUTING% echo WITH_UPSTREAM_LIBCOAP=%WITH_UPSTREAM_LIBCOAP% + echo MSVC_VERSION=%MSVC_VERSION% echo.scons VERBOSE=1 %BUILD_OPTIONS% scons VERBOSE=1 %BUILD_OPTIONS% ) else if "!RUN_ARG!"=="clean" ( @@ -152,11 +153,12 @@ if "!RUN_ARG!"=="server" ( erase resource\c_common\iotivity_config.h erase extlibs\libcoap\coap.lib erase extlibs\libcoap\libcoap\include\coap\coap_config.h + erase extlibs\mbedtls\mbed*.lib ) else if "!RUN_ARG!"=="cleangtest" ( rd /s /q extlibs\gtest\gtest-1.7.0 del extlibs\gtest\gtest-1.7.0.zip ) else ( - echo %0 - Script requires a valid argument! + echo.%0 - Script requires a valid argument! goto :EOF ) @@ -170,11 +172,11 @@ goto EOF echo %0 - Helper to build/test iotivity. Requires an argument. echo Installation: Drop this into your iotivity root directory to use it. echo. -echo. Default buidl settings are: debug binaries run unittests and no logging +echo. Default build settings are: debug binaries run unittests and no logging echo. echo. Default build parameters can be overridden using the following arguments echo. -echo -arch [x86 | amd64] - Build either amd64 or x86 architecture binaries +echo -arch [x86 ^| amd64] - Build either amd64 or x86 architecture binaries echo. echo -noTest - Don't run the unittests after building the binaries echo. -- 2.7.4