From a7a431b3092e31246911f7d30194b84a006b7f88 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Sun, 4 Aug 2013 22:17:07 -0600 Subject: iphlpapi: Implement NotifyAddrChange on Linux. --- configure.ac | 2 + dlls/iphlpapi/iphlpapi_main.c | 171 ++++++++++++++++++++++++++++++++++++++-- dlls/iphlpapi/tests/iphlpapi.c | 4 +- dlls/ws2_32/tests/sock.c | 2 +- 4 files changed, 171 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 0e3df61..184505a 100644 --- a/configure.ac +++ b/configure.ac @@ -437,7 +437,9 @@ AC_CHECK_HEADERS(\ linux/ioctl.h \ linux/joystick.h \ linux/major.h \ + linux/netlink.h \ linux/param.h \ + linux/rtnetlink.h \ linux/serial.h \ linux/types.h \ linux/ucdrom.h \ diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 837eb3d..15cfcda 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -38,6 +38,8 @@ #define NONAMELESSUNION #define NONAMELESSSTRUCT +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winreg.h" @@ -50,9 +52,18 @@ #include "ipstats.h" #include "ipifcons.h" #include "fltdefs.h" +#include "unistd.h" +#include "wine/server.h" #include "wine/debug.h" +#ifdef HAVE_LINUX_NETLINK_H +# include +#endif +#ifdef HAVE_LINUX_RTNETLINK_H +# include +#endif + WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi); #ifndef IF_NAMESIZE @@ -2027,6 +2038,118 @@ DWORD WINAPI IpRenewAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo) } +/*********************************************************************** + * IPHLPAPI_createmonitorhandle (INTERNAL) + * + * Routine to create the interface monitoring handle for NotifyAddrChange requests. + */ +static NTSTATUS IPHLPAPI_createmonitorhandle(HANDLE *h) +{ + NTSTATUS status = ERROR_NOT_SUPPORTED; + + *h = INVALID_HANDLE_VALUE; +#if defined(NETLINK_ROUTE) + SERVER_START_REQ( create_socket ) + { + req->family = PF_NETLINK; + req->type = SOCK_RAW; + req->protocol = NETLINK_ROUTE; + req->access = GENERIC_READ|SYNCHRONIZE; + req->attributes = OBJ_INHERIT; + req->flags = WSA_FLAG_OVERLAPPED; + status = wine_server_call( req ); + *h = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; +#else + FIXME("Interface monitoring is not currently supported on this platform.\n"); +#endif + return status; +} + + +/*********************************************************************** + * IPHLPAPI_initmonitorhandle (INTERNAL) + * + * Routine to setup the interface monitoring handle for NotifyAddrChange requests. + */ +static NTSTATUS IPHLPAPI_initmonitorhandle(HANDLE h) +{ + NTSTATUS status = ERROR_NOT_SUPPORTED; + int fd = -1; + + status = wine_server_handle_to_fd( h, FILE_READ_DATA, &fd, NULL ); + if (status == STATUS_SUCCESS) + { +#if defined(NETLINK_ROUTE) + struct sockaddr_nl addr; + + memset( &addr, 0, sizeof(addr) ); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV4_IFADDR; + if (bind( fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1) + status = ERROR_NOT_SUPPORTED; +#endif + } + wine_server_release_fd( h, fd ); + return status; +} + + +/*********************************************************************** + * IPHLPAPI_monitorifchange (INTERNAL) + * + * Routine to detect interface changes for NotifyAddrChange requests. + */ +static NTSTATUS IPHLPAPI_monitorifchange(HANDLE h) +{ + NTSTATUS status = ERROR_NOT_SUPPORTED; + char buffer[4096]; + int fd = -1; + size_t len; + + status = wine_server_handle_to_fd( h, FILE_READ_DATA, &fd, NULL ); + if (status != STATUS_SUCCESS) + return status; + status = STATUS_PENDING; + if ((len = recv( fd, buffer, sizeof(buffer), 0 )) != 0) + { +#if defined(NETLINK_ROUTE) + struct nlmsghdr *nlh; + + nlh = (struct nlmsghdr*) buffer; + if (NLMSG_OK(nlh, len) && (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR)) + status = STATUS_SUCCESS; +#endif + } + wine_server_release_fd( h, fd ); + return status; +} + + +/*********************************************************************** + * IPHLPAPI_apc_NotifyAddrChange (INTERNAL) + * + * APC for handling NotifyAddrChange requests. + */ +static NTSTATUS IPHLPAPI_apc_NotifyAddrChange(void *arg, IO_STATUS_BLOCK *iosb, NTSTATUS status, void **apc) +{ + LPOVERLAPPED overlapped = (LPOVERLAPPED) iosb; + HANDLE h = (HANDLE) arg; + + if (status != STATUS_ALERTED) + return status; + status = IPHLPAPI_monitorifchange( h ); + if (iosb) + { + iosb->u.Status = status; + if (overlapped->hEvent) + SetEvent( overlapped->hEvent ); + } + return status; +} + + /****************************************************************** * NotifyAddrChange (IPHLPAPI.@) * @@ -2043,12 +2166,50 @@ DWORD WINAPI IpRenewAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo) * FIXME * Stub, returns ERROR_NOT_SUPPORTED. */ -DWORD WINAPI NotifyAddrChange(PHANDLE Handle, LPOVERLAPPED overlapped) +DWORD WINAPI NotifyAddrChange(PHANDLE handle, LPOVERLAPPED overlapped) { - FIXME("(Handle %p, overlapped %p): stub\n", Handle, overlapped); - if (Handle) *Handle = INVALID_HANDLE_VALUE; - if (overlapped) ((IO_STATUS_BLOCK *) overlapped)->u.Status = STATUS_PENDING; - return ERROR_IO_PENDING; + IO_STATUS_BLOCK *iosb = (IO_STATUS_BLOCK *) overlapped; + NTSTATUS status; + HANDLE h; + + TRACE("(handle %p, overlapped %p): stub\n", handle, overlapped); + + status = IPHLPAPI_createmonitorhandle( &h ); + if (status != STATUS_SUCCESS) + { + ERR("Could not create interface monitoring socket.\n"); + goto done; + } + status = IPHLPAPI_initmonitorhandle( h ); + if (status != STATUS_SUCCESS) + { + ERR("Could not initialize interface monitoring socket.\n"); + goto done; + } + + /* for non-overlapped operation keep waiting until an interface change occurs */ + if (!overlapped) + { + while ((status = IPHLPAPI_monitorifchange( h )) == STATUS_PENDING) + Sleep( 0 ); + goto done; + } + /* for overlapped operation wait for the server to inform us that it's time to read data */ + *handle = h; + SERVER_START_REQ( register_async ) + { + req->type = ASYNC_TYPE_READ; + req->async.handle = wine_server_obj_handle( h ); + req->async.callback = wine_server_client_ptr( IPHLPAPI_apc_NotifyAddrChange ); + req->async.iosb = wine_server_client_ptr( iosb ); + req->async.arg = wine_server_client_ptr( h ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + +done: + if (iosb) iosb->u.Status = status; + return RtlNtStatusToDosError( status ); } diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index dcbd3ab..872a312 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1065,7 +1065,7 @@ static void testNotifyAddrChange(void) overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); ret = pNotifyAddrChange(&handle, &overlapped); ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %d, expected ERROR_IO_PENDING\n", ret); - todo_wine ok(handle != INVALID_HANDLE_VALUE, "NotifyAddrChange returned invalid file handle\n"); + ok(handle != INVALID_HANDLE_VALUE, "NotifyAddrChange returned invalid file handle\n"); success = GetOverlappedResult(handle, &overlapped, &bytes, FALSE); ok(success == FALSE, "GetOverlappedResult returned TRUE, expected FALSE\n"); ret = GetLastError(); @@ -1092,7 +1092,7 @@ static void testNotifyAddrChange(void) trace("Testing synchronous ipv4 address change notification. Please " "change the ipv4 address of one of your network interfaces\n"); ret = pNotifyAddrChange(NULL, NULL); - todo_wine ok(ret == NO_ERROR, "NotifyAddrChange returned %d, expected NO_ERROR\n", ret); + ok(ret == NO_ERROR, "NotifyAddrChange returned %d, expected NO_ERROR\n", ret); } } diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index d0e1bbc..467ba24 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -5550,7 +5550,7 @@ static void test_sioAddressListChange(void) " attached to %s (10 second timeout).\n", inet_ntoa(net_address)); ret = WaitForSingleObject(overlapped.hEvent, 10000); - todo_wine ok(ret == WAIT_OBJECT_0, "failed to get overlapped event %u\n", ret); + ok(ret == WAIT_OBJECT_0, "failed to get overlapped event %u\n", ret); end: closesocket(sock); -- 1.7.9.5