Further split out the SIO_ADDRESS_LIST_CHANGE patches.

This commit is contained in:
Erich E. Hoover 2014-04-04 11:34:29 -06:00
parent 9ebbbcfc9f
commit f13c0f3cd8
8 changed files with 585 additions and 488 deletions

View File

@ -1,481 +0,0 @@
From 60f7d242951be1980501f45922dbee5480ac2810 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Tue, 31 Dec 2013 18:36:58 -0700
Subject: server: Implement an interface change notification object.
---
server/event.c | 13 +++
server/named_pipe.c | 13 ---
server/object.h | 1 +
server/sock.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 334 insertions(+), 17 deletions(-)
diff --git a/server/event.c b/server/event.c
index b8515af..e8a3888 100644
--- a/server/event.c
+++ b/server/event.c
@@ -124,6 +124,19 @@ struct event *create_event( struct directory *root, const struct unicode_str *na
return event;
}
+obj_handle_t alloc_wait_event( struct process *process )
+{
+ obj_handle_t handle = 0;
+ struct event *event = create_event( NULL, NULL, 0, 1, 0, NULL );
+
+ if (event)
+ {
+ handle = alloc_handle( process, event, EVENT_ALL_ACCESS, 0 );
+ release_object( event );
+ }
+ return handle;
+}
+
struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access )
{
return (struct event *)get_handle_obj( process, handle, access, &event_ops );
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 4c85104..6ba2145 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -587,19 +587,6 @@ static enum server_fd_type pipe_client_get_fd_type( struct fd *fd )
return FD_TYPE_PIPE;
}
-static obj_handle_t alloc_wait_event( struct process *process )
-{
- obj_handle_t handle = 0;
- struct event *event = create_event( NULL, NULL, 0, 1, 0, NULL );
-
- if (event)
- {
- handle = alloc_handle( process, event, EVENT_ALL_ACCESS, 0 );
- release_object( event );
- }
- return handle;
-}
-
static obj_handle_t pipe_server_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async_data,
int blocking, const void *data, data_size_t size )
{
diff --git a/server/object.h b/server/object.h
index bb3ff21..bad162f 100644
--- a/server/object.h
+++ b/server/object.h
@@ -159,6 +159,7 @@ extern struct event *create_event( struct directory *root, const struct unicode_
const struct security_descriptor *sd );
extern struct keyed_event *create_keyed_event( struct directory *root, const struct unicode_str *name,
unsigned int attr, const struct security_descriptor *sd );
+extern obj_handle_t alloc_wait_event( struct process *process );
extern struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access );
extern struct keyed_event *get_keyed_event_obj( struct process *process, obj_handle_t handle, unsigned int access );
extern void pulse_event( struct event *event );
diff --git a/server/sock.c b/server/sock.c
index 1a3a8f7..9c8284d 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -23,6 +23,7 @@
#include "config.h"
+#include <limits.h>
#include <assert.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -44,11 +45,17 @@
#include <time.h>
#include <unistd.h>
+#ifdef HAVE_LINUX_RTNETLINK_H
+# include <linux/rtnetlink.h>
+#endif
+
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"
#include "winerror.h"
+#define USE_WS_PREFIX
+#include "winsock2.h"
#include "process.h"
#include "file.h"
@@ -83,9 +90,6 @@
#define FD_WINE_RAW 0x80000000
#define FD_WINE_INTERNAL 0xFFFF0000
-/* Constants for WSAIoctl() */
-#define WSA_FLAG_OVERLAPPED 0x01
-
struct sock
{
struct object obj; /* object header */
@@ -107,16 +111,28 @@ struct sock
struct sock *deferred; /* socket that waits for a deferred accept */
struct async_queue *read_q; /* queue for asynchronous reads */
struct async_queue *write_q; /* queue for asynchronous writes */
+ struct async_queue *ifchange_q; /* queue for interface change notifications */
+ struct list ifchange_entry; /* entry in ifchange notification list */
};
+#ifdef HAVE_LINUX_RTNETLINK_H
+/* only keep one ifchange object around, all sockets waiting for wakeups will look to it */
+static struct object *ifchange_object = NULL;
+
+static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data );
+#endif
+
static void sock_dump( struct object *obj, int verbose );
static int sock_signaled( struct object *obj, struct wait_queue_entry *entry );
static struct fd *sock_get_fd( struct object *obj );
static void sock_destroy( struct object *obj );
+static void sock_destroy_ifchange_q( struct sock *sock );
static int sock_get_poll_events( struct fd *fd );
static void sock_poll_event( struct fd *fd, int event );
static enum server_fd_type sock_get_fd_type( struct fd *fd );
+static obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async,
+ int blocking, const void *data, data_size_t size );
static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
static void sock_reselect_async( struct fd *fd, struct async_queue *queue );
static void sock_cancel_async( struct fd *fd, struct process *process, struct thread *thread, client_ptr_t iosb );
@@ -151,7 +167,7 @@ static const struct fd_ops sock_fd_ops =
sock_poll_event, /* poll_event */
no_flush, /* flush */
sock_get_fd_type, /* get_fd_type */
- default_fd_ioctl, /* ioctl */
+ sock_ioctl, /* ioctl */
sock_queue_async, /* queue_async */
sock_reselect_async, /* reselect_async */
sock_cancel_async /* cancel_async */
@@ -518,6 +534,41 @@ static enum server_fd_type sock_get_fd_type( struct fd *fd )
return FD_TYPE_SOCKET;
}
+obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async_data,
+ int blocking, const void *data, data_size_t size )
+{
+#ifdef HAVE_LINUX_RTNETLINK_H
+ struct sock *sock = get_fd_user( fd );
+ obj_handle_t wait_handle = 0;
+ async_data_t new_data;
+
+ assert( sock->obj.ops == &sock_ops );
+
+ if (blocking)
+ {
+ if (!(wait_handle = alloc_wait_event( current->process ))) return 0;
+ new_data = *async_data;
+ new_data.event = wait_handle;
+ async_data = &new_data;
+ }
+ switch(code)
+ {
+ case WS_SIO_ADDRESS_LIST_CHANGE:
+ if (sock_add_ifchange( sock, async_data ))
+ {
+ set_error( STATUS_PENDING );
+ return wait_handle;
+ }
+ break;
+ default:
+ /* handled by default_fd_ioctl */
+ break;
+ }
+ close_handle( current->process, wait_handle );
+#endif
+ return default_fd_ioctl(fd, code, async_data, blocking, data, size);
+}
+
static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
{
struct sock *sock = get_fd_user( fd );
@@ -592,6 +643,7 @@ static void sock_destroy( struct object *obj )
free_async_queue( sock->read_q );
free_async_queue( sock->write_q );
+ sock_destroy_ifchange_q( sock );
if (sock->event) release_object( sock->event );
if (sock->fd)
{
@@ -618,6 +670,7 @@ static void init_sock(struct sock *sock)
sock->deferred = NULL;
sock->read_q = NULL;
sock->write_q = NULL;
+ sock->ifchange_q = NULL;
memset( sock->errors, 0, sizeof(sock->errors) );
}
@@ -906,6 +959,269 @@ static void sock_set_error(void)
set_error( sock_get_ntstatus( errno ) );
}
+#ifdef HAVE_LINUX_RTNETLINK_H
+
+static void ifchange_dump( struct object *obj, int verbose );
+static struct fd *ifchange_get_fd( struct object *obj );
+static void ifchange_destroy( struct object *obj );
+
+static int ifchange_get_poll_events( struct fd *fd );
+static void ifchange_poll_event( struct fd *fd, int event );
+static void ifchange_reselect_async( struct fd *fd, struct async_queue *queue );
+
+struct ifchange
+{
+ struct object obj; /* object header */
+ struct fd *fd; /* interface change file descriptor */
+ struct list sockets; /* list of sockets to send interface change notifications */
+};
+
+static const struct object_ops ifchange_ops =
+{
+ sizeof(struct ifchange), /* size */
+ ifchange_dump, /* dump */
+ no_get_type, /* get_type */
+ add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ no_satisfied, /* satisfied */
+ no_signal, /* signal */
+ ifchange_get_fd, /* get_fd */
+ default_fd_map_access, /* map_access */
+ default_get_sd, /* get_sd */
+ default_set_sd, /* set_sd */
+ no_lookup_name, /* lookup_name */
+ no_open_file, /* open_file */
+ no_close_handle, /* close_handle */
+ ifchange_destroy /* destroy */
+};
+
+static const struct fd_ops ifchange_fd_ops =
+{
+ ifchange_get_poll_events, /* get_poll_events */
+ ifchange_poll_event, /* poll_event */
+ NULL, /* flush */
+ NULL, /* get_fd_type */
+ NULL, /* ioctl */
+ NULL, /* queue_async */
+ ifchange_reselect_async, /* reselect_async */
+ NULL /* cancel_async */
+};
+
+static void ifchange_dump( struct object *obj, int verbose )
+{
+ assert( obj->ops == &ifchange_ops );
+ printf( "ifchange\n" );
+}
+
+static struct fd *ifchange_get_fd( struct object *obj )
+{
+ struct ifchange *ifchange = (struct ifchange *)obj;
+ return (struct fd *)grab_object( ifchange->fd );
+}
+
+static void ifchange_destroy( struct object *obj )
+{
+ struct ifchange *ifchange = (struct ifchange *)obj;
+ assert( obj->ops == &ifchange_ops );
+
+ if (ifchange->fd)
+ {
+ /* reset the global ifchange object so that it will be recreated if it is needed again */
+ assert( obj == ifchange_object );
+ ifchange_object = NULL;
+
+ /* shut the socket down to force pending poll() calls in the client to return */
+ shutdown( get_unix_fd(ifchange->fd), SHUT_RDWR );
+ release_object( ifchange->fd );
+ }
+}
+
+static int ifchange_get_poll_events( struct fd *fd )
+{
+ return POLLIN;
+}
+
+static void ifchange_add_sock( struct object *obj, struct sock *sock )
+{
+ struct ifchange *ifchange = (struct ifchange *)obj;
+
+ list_add_tail( &ifchange->sockets, &sock->ifchange_entry );
+}
+
+/* we only need one of these interface notification objects, all of the sockets dependent upon
+ * it will wake up when a notification event occurs */
+static struct object *grab_ifchange( void )
+{
+ struct ifchange *ifchange;
+ struct sockaddr_nl addr;
+ int unix_fd;
+
+ if (ifchange_object)
+ {
+ /* increment the refcount for each socket that uses the ifchange object */
+ return grab_object( ifchange_object );
+ }
+
+ /* create the socket we need for processing interface change notifications */
+ unix_fd = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
+ if (unix_fd == -1)
+ {
+ sock_set_error();
+ return NULL;
+ }
+ fcntl( unix_fd, F_SETFL, O_NONBLOCK ); /* make socket nonblocking */
+ memset( &addr, 0, sizeof(addr) );
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_IPV4_IFADDR;
+ /* bind the socket to the special netlink kernel interface */
+ if (bind( unix_fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1)
+ {
+ sock_set_error();
+ close( unix_fd );
+ return NULL;
+ }
+ if (!(ifchange = alloc_object( &ifchange_ops )))
+ {
+ set_error( STATUS_NO_MEMORY );
+ close( unix_fd );
+ return NULL;
+ }
+ list_init( &ifchange->sockets );
+ if (!(ifchange->fd = create_anonymous_fd( &ifchange_fd_ops, unix_fd, &ifchange->obj, 0 )))
+ {
+ set_error( STATUS_NO_MEMORY );
+ release_object( ifchange );
+ return NULL;
+ }
+ set_fd_events( ifchange->fd, POLLIN ); /* enable read wakeup on the file descriptor */
+
+ /* the ifchange object is now successfully configured */
+ ifchange_object = &ifchange->obj;
+ return ifchange_object;
+}
+
+/* create a new ifchange queue for a specific socket or, if one already exists, reuse the existing one */
+static struct async_queue *sock_get_ifchange_q( struct sock *sock )
+{
+ struct object *ifchange;
+ struct fd *fd;
+
+ if (sock->ifchange_q) /* reuse existing ifchange_q for this socket */
+ return sock->ifchange_q;
+
+ if (!(ifchange = grab_ifchange()))
+ return NULL;
+
+ /* create the ifchange notification queue */
+ fd = ifchange_get_fd( ifchange );
+ sock->ifchange_q = create_async_queue( fd );
+ release_object( fd );
+ if (!sock->ifchange_q)
+ {
+ set_error( STATUS_NO_MEMORY );
+ release_object( ifchange );
+ return NULL;
+ }
+
+ /* add the socket to the ifchange notification list */
+ ifchange_add_sock( ifchange, sock );
+ return sock->ifchange_q;
+}
+
+/* wake up all the sockets waiting for a change notification event */
+static void ifchange_wake_up( struct object *obj, unsigned int status )
+{
+ struct ifchange *ifchange = (struct ifchange *)obj;
+ struct list *ptr, *next;
+ assert( obj->ops == &ifchange_ops );
+ assert( obj == ifchange_object );
+
+ LIST_FOR_EACH_SAFE( ptr, next, &ifchange->sockets )
+ {
+ struct sock *sock = LIST_ENTRY( ptr, struct sock, ifchange_entry );
+
+ assert( sock->ifchange_q );
+ async_wake_up( sock->ifchange_q, status ); /* issue ifchange notification for the socket */
+ sock_destroy_ifchange_q( sock ); /* remove socket from list and decrement ifchange refcount */
+ }
+}
+
+static void ifchange_poll_event( struct fd *fd, int event )
+{
+ struct object *ifchange = get_fd_user( fd );
+ unsigned int status = STATUS_PENDING;
+ char buffer[PIPE_BUF];
+ int r;
+
+ r = recv( get_unix_fd(fd), buffer, sizeof(buffer), MSG_DONTWAIT );
+ if (r < 0)
+ {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return; /* retry when poll() says the socket is ready */
+ status = sock_get_ntstatus( errno );
+ }
+ else if (r > 0)
+ {
+ struct nlmsghdr *nlh;
+
+ for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, r); nlh = NLMSG_NEXT(nlh, r))
+ {
+ if (nlh->nlmsg_type == NLMSG_DONE)
+ break;
+ if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR)
+ status = STATUS_SUCCESS;
+ }
+ }
+ if (status != STATUS_PENDING)
+ ifchange_wake_up( ifchange, status );
+}
+
+static void ifchange_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+ /* do nothing, this object is about to disappear */
+}
+
+/* add interface change notification to a socket */
+static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data )
+{
+ struct async_queue *ifchange_q;
+ struct async *async;
+
+ if (!(ifchange_q = sock_get_ifchange_q( sock )))
+ return FALSE;
+
+ if (!(async = create_async( current, ifchange_q, async_data )))
+ {
+ if (!async_queued( ifchange_q ))
+ sock_destroy_ifchange_q( sock );
+
+ set_error( STATUS_NO_MEMORY );
+ return FALSE;
+ }
+
+ release_object( async );
+ return TRUE;
+}
+
+#endif
+
+/* destroy an existing ifchange queue for a specific socket */
+static void sock_destroy_ifchange_q( struct sock *sock )
+{
+#ifdef HAVE_LINUX_RTNETLINK_H
+ if (sock->ifchange_q)
+ {
+ assert( ifchange_object );
+
+ list_remove( &sock->ifchange_entry );
+ free_async_queue( sock->ifchange_q );
+ sock->ifchange_q = NULL;
+ release_object( ifchange_object );
+ }
+#endif
+}
+
/* create a socket */
DECL_HANDLER(create_socket)
{
--
1.7.9.5

View File

@ -0,0 +1,77 @@
From a0625ca7d07703028fdad511c37c587e213ff481 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 3 Apr 2014 09:21:51 -0600
Subject: server: Implement socket-specific ioctl() routine.
---
server/sock.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/server/sock.c b/server/sock.c
index 5ffb1fe..3eb1bdf 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -49,6 +49,8 @@
#include "windef.h"
#include "winternl.h"
#include "winerror.h"
+#define USE_WS_PREFIX
+#include "winsock2.h"
#include "process.h"
#include "file.h"
@@ -83,9 +85,6 @@
#define FD_WINE_RAW 0x80000000
#define FD_WINE_INTERNAL 0xFFFF0000
-/* Constants for WSAIoctl() */
-#define WSA_FLAG_OVERLAPPED 0x01
-
struct sock
{
struct object obj; /* object header */
@@ -117,6 +116,8 @@ static void sock_destroy( struct object *obj );
static int sock_get_poll_events( struct fd *fd );
static void sock_poll_event( struct fd *fd, int event );
static enum server_fd_type sock_get_fd_type( struct fd *fd );
+static obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async,
+ int blocking, const void *data, data_size_t size );
static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
static void sock_reselect_async( struct fd *fd, struct async_queue *queue );
static void sock_cancel_async( struct fd *fd, struct process *process, struct thread *thread, client_ptr_t iosb );
@@ -151,7 +152,7 @@ static const struct fd_ops sock_fd_ops =
sock_poll_event, /* poll_event */
no_flush, /* flush */
sock_get_fd_type, /* get_fd_type */
- default_fd_ioctl, /* ioctl */
+ sock_ioctl, /* ioctl */
sock_queue_async, /* queue_async */
sock_reselect_async, /* reselect_async */
sock_cancel_async /* cancel_async */
@@ -518,6 +519,23 @@ static enum server_fd_type sock_get_fd_type( struct fd *fd )
return FD_TYPE_SOCKET;
}
+obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async_data,
+ int blocking, const void *data, data_size_t size )
+{
+ struct sock *sock = get_fd_user( fd );
+
+ assert( sock->obj.ops == &sock_ops );
+
+ switch(code)
+ {
+ case WS_SIO_ADDRESS_LIST_CHANGE:
+ /* intentional fallthrough, not yet supported */
+ default:
+ set_error( STATUS_NOT_SUPPORTED );
+ return 0;
+ }
+}
+
static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
{
struct sock *sock = get_fd_user( fd );
--
1.7.9.5

View File

@ -0,0 +1,114 @@
From 45dfbd2ddb5ca2c64fcfd56392be9c55513abc4b Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 3 Apr 2014 09:23:02 -0600
Subject: server: Add delayed processing for socket-specific ioctl().
---
server/event.c | 13 +++++++++++++
server/named_pipe.c | 13 -------------
server/object.h | 1 +
server/sock.c | 19 +++++++++++++++++--
4 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/server/event.c b/server/event.c
index 4d3c562..0daa5b2 100644
--- a/server/event.c
+++ b/server/event.c
@@ -124,6 +124,19 @@ struct event *create_event( struct directory *root, const struct unicode_str *na
return event;
}
+obj_handle_t alloc_wait_event( struct process *process )
+{
+ obj_handle_t handle = 0;
+ struct event *event = create_event( NULL, NULL, 0, 1, 0, NULL );
+
+ if (event)
+ {
+ handle = alloc_handle( process, event, EVENT_ALL_ACCESS, 0 );
+ release_object( event );
+ }
+ return handle;
+}
+
struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access )
{
return (struct event *)get_handle_obj( process, handle, access, &event_ops );
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 4c85104..6ba2145 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -587,19 +587,6 @@ static enum server_fd_type pipe_client_get_fd_type( struct fd *fd )
return FD_TYPE_PIPE;
}
-static obj_handle_t alloc_wait_event( struct process *process )
-{
- obj_handle_t handle = 0;
- struct event *event = create_event( NULL, NULL, 0, 1, 0, NULL );
-
- if (event)
- {
- handle = alloc_handle( process, event, EVENT_ALL_ACCESS, 0 );
- release_object( event );
- }
- return handle;
-}
-
static obj_handle_t pipe_server_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async_data,
int blocking, const void *data, data_size_t size )
{
diff --git a/server/object.h b/server/object.h
index bb3ff21..bad162f 100644
--- a/server/object.h
+++ b/server/object.h
@@ -159,6 +159,7 @@ extern struct event *create_event( struct directory *root, const struct unicode_
const struct security_descriptor *sd );
extern struct keyed_event *create_keyed_event( struct directory *root, const struct unicode_str *name,
unsigned int attr, const struct security_descriptor *sd );
+extern obj_handle_t alloc_wait_event( struct process *process );
extern struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access );
extern struct keyed_event *get_keyed_event_obj( struct process *process, obj_handle_t handle, unsigned int access );
extern void pulse_event( struct event *event );
diff --git a/server/sock.c b/server/sock.c
index 3eb1bdf..05fc38b 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -523,17 +523,32 @@ obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *a
int blocking, const void *data, data_size_t size )
{
struct sock *sock = get_fd_user( fd );
+ obj_handle_t wait_handle = 0;
+ async_data_t new_data;
+ int error;
assert( sock->obj.ops == &sock_ops );
+ if (blocking)
+ {
+ if (!(wait_handle = alloc_wait_event( current->process ))) return 0;
+ new_data = *async_data;
+ new_data.event = wait_handle;
+ async_data = &new_data;
+ }
switch(code)
{
case WS_SIO_ADDRESS_LIST_CHANGE:
/* intentional fallthrough, not yet supported */
default:
- set_error( STATUS_NOT_SUPPORTED );
- return 0;
+ error = STATUS_NOT_SUPPORTED;
+ break;
}
+ set_error( error );
+ if (error == STATUS_PENDING)
+ return wait_handle;
+ close_handle( current->process, wait_handle );
+ return 0;
}
static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
--
1.7.9.5

View File

@ -0,0 +1,150 @@
From 69e77bd226057f486d1c076ccbebb963d3b750ee Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 3 Apr 2014 09:25:48 -0600
Subject: server: Add socket-side support for the interface change
notification object.
---
server/sock.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 91 insertions(+), 1 deletion(-)
diff --git a/server/sock.c b/server/sock.c
index 05fc38b..20d022f 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -106,12 +106,18 @@ struct sock
struct sock *deferred; /* socket that waits for a deferred accept */
struct async_queue *read_q; /* queue for asynchronous reads */
struct async_queue *write_q; /* queue for asynchronous writes */
+ struct async_queue *ifchange_q; /* queue for interface change notifications */
+ struct object *ifchange_obj; /* the interface change notification object */
+ struct list ifchange_entry; /* entry in ifchange notification list */
};
static void sock_dump( struct object *obj, int verbose );
+static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data );
static int sock_signaled( struct object *obj, struct wait_queue_entry *entry );
static struct fd *sock_get_fd( struct object *obj );
static void sock_destroy( struct object *obj );
+static int sock_get_ifchange_q( struct sock *sock, struct async_queue **async_queue );
+static void sock_destroy_ifchange_q( struct sock *sock );
static int sock_get_poll_events( struct fd *fd );
static void sock_poll_event( struct fd *fd, int event );
@@ -539,7 +545,8 @@ obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *a
switch(code)
{
case WS_SIO_ADDRESS_LIST_CHANGE:
- /* intentional fallthrough, not yet supported */
+ error = sock_add_ifchange( sock, async_data );
+ break;
default:
error = STATUS_NOT_SUPPORTED;
break;
@@ -625,6 +632,7 @@ static void sock_destroy( struct object *obj )
free_async_queue( sock->read_q );
free_async_queue( sock->write_q );
+ sock_destroy_ifchange_q( sock );
if (sock->event) release_object( sock->event );
if (sock->fd)
{
@@ -651,6 +659,8 @@ static void init_sock(struct sock *sock)
sock->deferred = NULL;
sock->read_q = NULL;
sock->write_q = NULL;
+ sock->ifchange_q = NULL;
+ sock->ifchange_obj = NULL;
memset( sock->errors, 0, sizeof(sock->errors) );
}
@@ -939,6 +949,86 @@ static void sock_set_error(void)
set_error( sock_get_ntstatus( errno ) );
}
+/* add interface change notification to a socket */
+static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data )
+{
+ struct async_queue *ifchange_q = NULL;
+ struct async *async;
+ int error;
+
+ error = sock_get_ifchange_q( sock, &ifchange_q );
+ if (error != STATUS_PENDING)
+ return error;
+
+ if (!(async = create_async( current, ifchange_q, async_data )))
+ {
+ if (!async_queued( ifchange_q ))
+ sock_destroy_ifchange_q( sock );
+
+ return STATUS_NO_MEMORY;
+ }
+
+ release_object( async );
+ return error;
+}
+
+/* stub ifchange object */
+static int get_ifchange( struct object **obj )
+{
+ return STATUS_NOT_SUPPORTED;
+}
+
+/* stub ifchange add socket to list */
+static void ifchange_add_sock( struct object *obj, struct sock *sock )
+{
+}
+
+/* create a new ifchange queue for a specific socket or, if one already exists, reuse the existing one */
+static int sock_get_ifchange_q( struct sock *sock, struct async_queue **async_queue )
+{
+ struct object *ifchange = NULL;
+ struct fd *fd;
+ int error;
+
+ if (sock->ifchange_q) /* reuse existing ifchange_q for this socket */
+ {
+ *async_queue = sock->ifchange_q;
+ return STATUS_PENDING;
+ }
+
+ error = get_ifchange( &ifchange );
+ if (error != STATUS_PENDING)
+ return error;
+
+ /* create the ifchange notification queue */
+ fd = ifchange->ops->get_fd( ifchange );
+ sock->ifchange_q = create_async_queue( fd );
+ release_object( fd );
+ if (!sock->ifchange_q)
+ {
+ release_object( ifchange );
+ return STATUS_NO_MEMORY;
+ }
+
+ /* add the socket to the ifchange notification list */
+ ifchange_add_sock( ifchange, sock );
+ sock->ifchange_obj = ifchange;
+ *async_queue = sock->ifchange_q;
+ return error;
+}
+
+/* destroy an existing ifchange queue for a specific socket */
+static void sock_destroy_ifchange_q( struct sock *sock )
+{
+ if (sock->ifchange_q)
+ {
+ list_remove( &sock->ifchange_entry );
+ free_async_queue( sock->ifchange_q );
+ sock->ifchange_q = NULL;
+ release_object( sock->ifchange_obj );
+ }
+}
+
/* create a socket */
DECL_HANDLER(create_socket)
{
--
1.7.9.5

View File

@ -0,0 +1,237 @@
From 85d0475a493be336c340f25cab9895846e202f26 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 3 Apr 2014 09:26:34 -0600
Subject: server: Implement the interface change notification object.
---
server/sock.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 198 insertions(+), 2 deletions(-)
diff --git a/server/sock.c b/server/sock.c
index 20d022f..82464f2 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -43,6 +43,10 @@
#endif
#include <time.h>
#include <unistd.h>
+#include <limits.h>
+#ifdef HAVE_LINUX_RTNETLINK_H
+# include <linux/rtnetlink.h>
+#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -972,15 +976,207 @@ static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data
return error;
}
-/* stub ifchange object */
+#ifdef HAVE_LINUX_RTNETLINK_H
+
+/* only keep one ifchange object around, all sockets waiting for wakeups will look to it */
+static struct object *ifchange_object = NULL;
+
+static void ifchange_dump( struct object *obj, int verbose );
+static struct fd *ifchange_get_fd( struct object *obj );
+static void ifchange_destroy( struct object *obj );
+
+static int ifchange_get_poll_events( struct fd *fd );
+static void ifchange_poll_event( struct fd *fd, int event );
+static void ifchange_reselect_async( struct fd *fd, struct async_queue *queue );
+
+struct ifchange
+{
+ struct object obj; /* object header */
+ struct fd *fd; /* interface change file descriptor */
+ struct list sockets; /* list of sockets to send interface change notifications */
+};
+
+static const struct object_ops ifchange_ops =
+{
+ sizeof(struct ifchange), /* size */
+ ifchange_dump, /* dump */
+ no_get_type, /* get_type */
+ add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ no_satisfied, /* satisfied */
+ no_signal, /* signal */
+ ifchange_get_fd, /* get_fd */
+ default_fd_map_access, /* map_access */
+ default_get_sd, /* get_sd */
+ default_set_sd, /* set_sd */
+ no_lookup_name, /* lookup_name */
+ no_open_file, /* open_file */
+ no_close_handle, /* close_handle */
+ ifchange_destroy /* destroy */
+};
+
+static const struct fd_ops ifchange_fd_ops =
+{
+ ifchange_get_poll_events, /* get_poll_events */
+ ifchange_poll_event, /* poll_event */
+ NULL, /* flush */
+ NULL, /* get_fd_type */
+ NULL, /* ioctl */
+ NULL, /* queue_async */
+ ifchange_reselect_async, /* reselect_async */
+ NULL /* cancel_async */
+};
+
+static void ifchange_dump( struct object *obj, int verbose )
+{
+ assert( obj->ops == &ifchange_ops );
+ printf( "ifchange\n" );
+}
+
+static struct fd *ifchange_get_fd( struct object *obj )
+{
+ struct ifchange *ifchange = (struct ifchange *)obj;
+ return (struct fd *)grab_object( ifchange->fd );
+}
+
+static void ifchange_destroy( struct object *obj )
+{
+ struct ifchange *ifchange = (struct ifchange *)obj;
+ assert( obj->ops == &ifchange_ops );
+
+ if (ifchange->fd)
+ {
+ /* reset the global ifchange object so that it will be recreated if it is needed again */
+ assert( obj == ifchange_object );
+ ifchange_object = NULL;
+
+ /* shut the socket down to force pending poll() calls in the client to return */
+ shutdown( get_unix_fd(ifchange->fd), SHUT_RDWR );
+ release_object( ifchange->fd );
+ }
+}
+
+static int ifchange_get_poll_events( struct fd *fd )
+{
+ return POLLIN;
+}
+
+/* wake up all the sockets waiting for a change notification event */
+static void ifchange_wake_up( struct object *obj, unsigned int status )
+{
+ struct ifchange *ifchange = (struct ifchange *)obj;
+ struct list *ptr, *next;
+ assert( obj->ops == &ifchange_ops );
+ assert( obj == ifchange_object );
+
+ LIST_FOR_EACH_SAFE( ptr, next, &ifchange->sockets )
+ {
+ struct sock *sock = LIST_ENTRY( ptr, struct sock, ifchange_entry );
+
+ assert( sock->ifchange_q );
+ async_wake_up( sock->ifchange_q, status ); /* issue ifchange notification for the socket */
+ sock_destroy_ifchange_q( sock ); /* remove socket from list and decrement ifchange refcount */
+ }
+}
+
+static void ifchange_poll_event( struct fd *fd, int event )
+{
+ struct object *ifchange = get_fd_user( fd );
+ unsigned int status = STATUS_PENDING;
+ char buffer[PIPE_BUF];
+ int r;
+
+ r = recv( get_unix_fd(fd), buffer, sizeof(buffer), MSG_DONTWAIT );
+ if (r < 0)
+ {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return; /* retry when poll() says the socket is ready */
+ status = sock_get_ntstatus( errno );
+ }
+ else if (r > 0)
+ {
+ struct nlmsghdr *nlh;
+
+ for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, r); nlh = NLMSG_NEXT(nlh, r))
+ {
+ if (nlh->nlmsg_type == NLMSG_DONE)
+ break;
+ if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR)
+ status = STATUS_SUCCESS;
+ }
+ }
+ if (status != STATUS_PENDING)
+ ifchange_wake_up( ifchange, status );
+}
+
+static void ifchange_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+ /* do nothing, this object is about to disappear */
+}
+
+#endif
+
+/* we only need one of these interface notification objects, all of the sockets dependent upon
+ * it will wake up when a notification event occurs */
static int get_ifchange( struct object **obj )
{
+#ifdef HAVE_LINUX_RTNETLINK_H
+ struct ifchange *ifchange;
+ struct sockaddr_nl addr;
+ int unix_fd;
+
+ if (ifchange_object)
+ {
+ /* increment the refcount for each socket that uses the ifchange object */
+ *obj = grab_object( ifchange_object );
+ return STATUS_PENDING;
+ }
+
+ /* create the socket we need for processing interface change notifications */
+ unix_fd = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
+ if (unix_fd == -1)
+ return sock_get_ntstatus( errno );
+ fcntl( unix_fd, F_SETFL, O_NONBLOCK ); /* make socket nonblocking */
+ memset( &addr, 0, sizeof(addr) );
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_IPV4_IFADDR;
+ /* bind the socket to the special netlink kernel interface */
+ if (bind( unix_fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1)
+ {
+ close( unix_fd );
+ return sock_get_ntstatus( errno );
+ }
+ if (!(ifchange = alloc_object( &ifchange_ops )))
+ {
+ close( unix_fd );
+ return STATUS_NO_MEMORY;
+ }
+ list_init( &ifchange->sockets );
+ if (!(ifchange->fd = create_anonymous_fd( &ifchange_fd_ops, unix_fd, &ifchange->obj, 0 )))
+ {
+ release_object( ifchange );
+ return STATUS_NO_MEMORY;
+ }
+ set_fd_events( ifchange->fd, POLLIN ); /* enable read wakeup on the file descriptor */
+
+ /* the ifchange object is now successfully configured */
+ ifchange_object = &ifchange->obj;
+ *obj = &ifchange->obj;
+ return STATUS_PENDING;
+#else
return STATUS_NOT_SUPPORTED;
+#endif
}
-/* stub ifchange add socket to list */
+/* add the socket to the interface change notification list */
static void ifchange_add_sock( struct object *obj, struct sock *sock )
{
+#ifdef HAVE_LINUX_RTNETLINK_H
+ struct ifchange *ifchange = (struct ifchange *)obj;
+
+ list_add_tail( &ifchange->sockets, &sock->ifchange_entry );
+#endif
}
/* create a new ifchange queue for a specific socket or, if one already exists, reuse the existing one */
--
1.7.9.5

View File

@ -1,6 +1,6 @@
From b3156dc253a94f9414a04569181728ec43608f2a Mon Sep 17 00:00:00 2001
From 621fe55cd32221c542df779540d3bc8ea701821d Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Mon, 18 Nov 2013 17:22:14 -0700
Date: Thu, 3 Apr 2014 09:26:45 -0600
Subject: ws2_32: Add an interactive test for interface change notifications.
---
@ -8,10 +8,10 @@ Subject: ws2_32: Add an interactive test for interface change notifications.
1 file changed, 68 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 0abf732..4703e59 100644
index dcedd99..e55273a 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -6487,6 +6487,73 @@ static void test_sioRoutingInterfaceQuery(void)
@@ -6607,6 +6607,73 @@ static void test_sioRoutingInterfaceQuery(void)
closesocket(sock);
}
@ -85,7 +85,7 @@ index 0abf732..4703e59 100644
static void test_synchronous_WSAIoctl(void)
{
HANDLE previous_port, io_port;
@@ -7570,6 +7637,7 @@ START_TEST( sock )
@@ -7694,6 +7761,7 @@ START_TEST( sock )
test_ConnectEx();
test_sioRoutingInterfaceQuery();

View File

@ -1,3 +1,3 @@
Revision: 1
Revision: 2
Author: Erich E. Hoover
Title: Implement SIO_ADDRESS_LIST_CHANGE.

View File

@ -46,7 +46,7 @@ index a273502..5fa0cd5 100644
+ const char *author;
+ const char *title;
+} wine_patch_data[] = {
+ { "8a366b6d-8ad6-4581-8aa9-66a03590a57b:1", "Erich E. Hoover", "Implement SIO_ADDRESS_LIST_CHANGE." },
+ { "8a366b6d-8ad6-4581-8aa9-66a03590a57b:2", "Erich E. Hoover", "Implement SIO_ADDRESS_LIST_CHANGE." },
+ { "92938b89-506b-430a-ba50-32de8b286e56:2", "Erich E. Hoover", "Store and return security attributes with extended file attributes." },
+ { "9cb0f665-bf7c-485f-89cc-554adcdf8880:2", "Erich E. Hoover", "Allow string comparison with linguistic casing." },
+ { "5d6bb7b5-ec88-4ed3-907d-9ad2173a2f88:1", "Sebastian Lackner", "Enable/disable windows when they are (un)mapped by foreign applications." },