From d5155e2dfe3756528bdae8b7221cd3e20a381d0f Mon Sep 17 00:00:00 2001
From: Erich Hoover <ehoover@mines.edu>
Date: Wed, 8 May 2013 15:32:23 -0600
Subject: iphlpapi: Implement NotifyAddrChange on Linux.

---
 configure.ac                   |    2 +
 dlls/iphlpapi/iphlpapi_main.c  |  134 ++++++++++++++++++++++++++++++++++++++--
 dlls/iphlpapi/tests/iphlpapi.c |    4 +-
 3 files changed, 133 insertions(+), 7 deletions(-)

diff --git a/configure.ac b/configure.ac
index 6dd2348..f2c61c9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -443,7 +443,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 aeb91c1..a7dbb3e 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,27 @@
 #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 <linux/netlink.h>
+#endif
+#ifdef HAVE_LINUX_RTNETLINK_H
+# include <linux/rtnetlink.h>
+#endif
+
+#ifdef NLMSG_OK
+# define WINE_LINKMON_FAMILY         PF_NETLINK
+# define WINE_LINKMON_TYPE           SOCK_RAW
+# define WINE_LINKMON_PROTO          NETLINK_ROUTE
+# define WINE_LINKMON_ADDRCHANGE(b)  (NLMSG_OK((struct nlmsghdr*)b, len) \
+             && (((struct nlmsghdr*)b)->nlmsg_type == RTM_NEWADDR \
+                 || ((struct nlmsghdr*)b)->nlmsg_type == RTM_DELADDR))
+#endif
+
 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
 
 #ifndef IF_NAMESIZE
@@ -2027,6 +2047,43 @@ DWORD WINAPI IpRenewAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo)
 }
 
 
+/***********************************************************************
+ *     IPHLPAPI_NotifyAddrChange           (INTERNAL)
+ * 
+ * APC for handling NotifyAddrChange requests.
+ */
+#ifdef WINE_LINKMON_FAMILY
+static NTSTATUS IPHLPAPI_NotifyAddrChange( void *arg, IO_STATUS_BLOCK *iosb, NTSTATUS status, void **apc )
+{
+    LPOVERLAPPED overlapped = (LPOVERLAPPED) iosb;
+    HANDLE h = (HANDLE) arg;
+    char buffer[4096];
+    int fd = -1;
+    size_t len;
+
+    if (status != STATUS_ALERTED)
+        return status;
+    if ((status = wine_server_handle_to_fd( h, FILE_READ_DATA, &fd, NULL ) ))
+        return status;
+    if ((len = recv( fd, buffer, sizeof(buffer), 0 )) == 0 || !WINE_LINKMON_ADDRCHANGE(buffer))
+    {
+        status = STATUS_PENDING;
+        goto done;
+    }
+    status = STATUS_SUCCESS;
+done:
+    if (iosb)
+    {
+        iosb->u.Status = status;
+        if (overlapped->hEvent)
+            SetEvent( overlapped->hEvent );
+    }
+    wine_server_release_fd( h, fd );
+    return status;
+}
+#endif
+
+
 /******************************************************************
  *    NotifyAddrChange (IPHLPAPI.@)
  *
@@ -2043,12 +2100,79 @@ 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;
+#ifdef WINE_LINKMON_FAMILY
+    IO_STATUS_BLOCK *iosb = (IO_STATUS_BLOCK *) overlapped;
+    struct sockaddr_nl addr;
+    NTSTATUS status;
+    int fd = -1;
+    HANDLE h;
+
+    TRACE("(handle %p, overlapped %p): stub\n", handle, overlapped);
+
+    h = INVALID_HANDLE_VALUE;
+    SERVER_START_REQ( create_socket )
+    {
+        req->family     = WINE_LINKMON_FAMILY;
+        req->type       = WINE_LINKMON_TYPE;
+        req->protocol   = WINE_LINKMON_PROTO;
+        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;
+    if (status != STATUS_SUCCESS)
+    {
+        ERR("Could not create interface monitoring socket.\n");
+        goto done;
+    }
+    status = wine_server_handle_to_fd( h, FILE_READ_DATA, &fd, NULL );
+    if (status != STATUS_SUCCESS)
+    {
+        ERR("Could not get handle to interface monitoring socket.\n");
+        goto done;
+    }
+    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)
+    {
+        ERR("Could not bind to interface monitoring socket.\n");
+        status = STATUS_NOT_SUPPORTED;
+        goto done;
+    }
+
+    if (!overlapped)
+    {
+        while ((status = IPHLPAPI_NotifyAddrChange( h, NULL, STATUS_ALERTED, NULL )) == 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_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:
+    wine_server_release_fd( h, fd );
+    if (iosb) iosb->u.Status = status;
+    return RtlNtStatusToDosError( status );
+#else
+    if (handle) *handle = INVALID_HANDLE_VALUE;
+    FIXME("Interface monitoring is not currently supported on this platform.\n");
+    return ERROR_NOT_SUPPORTED;
+#endif
 }
 
 
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c
index 26bf26e..684b1ec 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);
     }
 }
 
-- 
1.7.9.5

