From 2a461b26e92230c839d80828c35ca43823ecb559 Mon Sep 17 00:00:00 2001
From: Erich Hoover <ehoover@mines.edu>
Date: Sun, 19 Sep 2010 11:09:00 -0600
Subject: server: Add mechanism for storing an interface ID with a socket.

---
 include/wine/server_protocol.h |   36 +++++++++++-
 server/Makefile.in             |    6 +-
 server/protocol.def            |   15 +++++
 server/request.h               |   11 ++++
 server/sock.c                  |  131 +++++++++++++++++++++++++++++++++++++++-
 server/trace.c                 |   22 +++++++
 6 files changed, 216 insertions(+), 5 deletions(-)

diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index b699956..78c3147 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1393,6 +1393,34 @@ struct get_socket_event_reply
 
 
 
+struct set_socket_iface_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+    int iface;
+    char __pad_20[4];
+};
+struct set_socket_iface_reply
+{
+    struct reply_header __header;
+};
+
+
+
+struct get_socket_iface_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+};
+struct get_socket_iface_reply
+{
+    struct reply_header __header;
+    int iface;
+    char __pad_12[4];
+};
+
+
+
 struct enable_socket_event_request
 {
     struct request_header __header;
@@ -4819,6 +4847,8 @@ enum request
     REQ_accept_into_socket,
     REQ_set_socket_event,
     REQ_get_socket_event,
+    REQ_set_socket_iface,
+    REQ_get_socket_iface,
     REQ_enable_socket_event,
     REQ_set_socket_deferred,
     REQ_alloc_console,
@@ -5069,6 +5099,8 @@ union generic_request
     struct accept_into_socket_request accept_into_socket_request;
     struct set_socket_event_request set_socket_event_request;
     struct get_socket_event_request get_socket_event_request;
+    struct set_socket_iface_request set_socket_iface_request;
+    struct get_socket_iface_request get_socket_iface_request;
     struct enable_socket_event_request enable_socket_event_request;
     struct set_socket_deferred_request set_socket_deferred_request;
     struct alloc_console_request alloc_console_request;
@@ -5317,6 +5349,8 @@ union generic_reply
     struct accept_into_socket_reply accept_into_socket_reply;
     struct set_socket_event_reply set_socket_event_reply;
     struct get_socket_event_reply get_socket_event_reply;
+    struct set_socket_iface_reply set_socket_iface_reply;
+    struct get_socket_iface_reply get_socket_iface_reply;
     struct enable_socket_event_reply enable_socket_event_reply;
     struct set_socket_deferred_reply set_socket_deferred_reply;
     struct alloc_console_reply alloc_console_reply;
@@ -5515,6 +5549,6 @@ union generic_reply
     struct set_cursor_reply set_cursor_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 408
+#define SERVER_PROTOCOL_VERSION 410
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/Makefile.in b/server/Makefile.in
index 7a67707..6ee98af 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -4,7 +4,7 @@ TOPOBJDIR = ..
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = none
-EXTRALIBS = @LIBPOLL@
+EXTRALIBS = @LIBPTHREAD@
 
 C_SRCS = \
 	async.c \
@@ -65,10 +65,10 @@ all: $(PROGRAMS) $(MANPAGES)
 @MAKE_RULES@
 
 wineserver: $(OBJS)
-	$(CC) -o $@ $(OBJS) $(LIBWINE) $(LIBPORT) $(LDFLAGS) $(LIBS) $(LDRPATH_LOCAL)
+	$(CC) -o $@ $(OBJS) $(LIBWINE) $(LIBPORT) $(LDFLAGS) $(LIBS) $(EXTRALIBS) $(LDRPATH_LOCAL)
 
 wineserver-installed: $(OBJS)
-	$(CC) -o $@ $(OBJS) $(LIBWINE) $(LIBPORT) $(LDFLAGS) $(LIBS) $(LDRPATH_INSTALL)
+	$(CC) -o $@ $(OBJS) $(LIBWINE) $(LIBPORT) $(LDFLAGS) $(LIBS) $(EXTRALIBS) $(LDRPATH_INSTALL)
 
 install install-lib:: wineserver-installed $(MANPAGES) $(INSTALLDIRS)
 	$(INSTALL_PROGRAM) wineserver-installed $(DESTDIR)$(bindir)/wineserver
diff --git a/server/protocol.def b/server/protocol.def
index e3e078d..d226cef 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1119,6 +1119,21 @@ enum server_fd_type
 @END
 
 
+/* Set socket interface ID */
+@REQ(set_socket_iface)
+    obj_handle_t handle;        /* handle to the socket */
+    int iface;                  /* interface ID */
+@END
+
+
+/* Get socket interface ID */
+@REQ(get_socket_iface)
+    obj_handle_t handle;        /* handle to the socket */
+@REPLY
+    int iface;                  /* interface ID */
+@END
+
+
 /* Reenable pending socket events */
 @REQ(enable_socket_event)
     obj_handle_t handle;        /* handle to the socket */
diff --git a/server/request.h b/server/request.h
index 6cb37b3..3f6397b 100644
--- a/server/request.h
+++ b/server/request.h
@@ -158,6 +158,8 @@ DECL_HANDLER(accept_socket);
 DECL_HANDLER(accept_into_socket);
 DECL_HANDLER(set_socket_event);
 DECL_HANDLER(get_socket_event);
+DECL_HANDLER(set_socket_iface);
+DECL_HANDLER(get_socket_iface);
 DECL_HANDLER(enable_socket_event);
 DECL_HANDLER(set_socket_deferred);
 DECL_HANDLER(alloc_console);
@@ -407,6 +409,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_accept_into_socket,
     (req_handler)req_set_socket_event,
     (req_handler)req_get_socket_event,
+    (req_handler)req_set_socket_iface,
+    (req_handler)req_get_socket_iface,
     (req_handler)req_enable_socket_event,
     (req_handler)req_set_socket_deferred,
     (req_handler)req_alloc_console,
@@ -943,6 +947,13 @@ C_ASSERT( FIELD_OFFSET(struct get_socket_event_reply, mask) == 8 );
 C_ASSERT( FIELD_OFFSET(struct get_socket_event_reply, pmask) == 12 );
 C_ASSERT( FIELD_OFFSET(struct get_socket_event_reply, state) == 16 );
 C_ASSERT( sizeof(struct get_socket_event_reply) == 24 );
+C_ASSERT( FIELD_OFFSET(struct set_socket_iface_request, handle) == 12 );
+C_ASSERT( FIELD_OFFSET(struct set_socket_iface_request, iface) == 16 );
+C_ASSERT( sizeof(struct set_socket_iface_request) == 24 );
+C_ASSERT( FIELD_OFFSET(struct get_socket_iface_request, handle) == 12 );
+C_ASSERT( sizeof(struct get_socket_iface_request) == 16 );
+C_ASSERT( FIELD_OFFSET(struct get_socket_iface_reply, iface) == 8 );
+C_ASSERT( sizeof(struct get_socket_iface_reply) == 16 );
 C_ASSERT( FIELD_OFFSET(struct enable_socket_event_request, handle) == 12 );
 C_ASSERT( FIELD_OFFSET(struct enable_socket_event_request, mask) == 16 );
 C_ASSERT( FIELD_OFFSET(struct enable_socket_event_request, sstate) == 20 );
diff --git a/server/sock.c b/server/sock.c
index 04ae748..c6f22ff 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -38,14 +38,19 @@
 #ifdef HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
 #ifdef HAVE_SYS_FILIO_H
 # include <sys/filio.h>
 #endif
+#include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
+#include <semaphore.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -89,6 +94,10 @@
 /* Constants for WSAIoctl() */
 #define WSA_FLAG_OVERLAPPED        0x01
 
+#if defined(HAVE_NETINET_IN_H) && !defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
+#    define USE_IP_PKTINFO_FILTER
+#endif
+
 struct sock
 {
     struct object       obj;         /* object header */
@@ -109,6 +118,8 @@ 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 */
+    int                 iface_index; /* interface ID for interface-specific sockets */
+    sem_t              *iface_lock;  /* semaphore lock for interface-specific sockets */
 };
 
 static void sock_dump( struct object *obj, int verbose );
@@ -371,6 +382,62 @@ end:
     sock_wake_up( sock );
 }
 
+/* Test for IP_PKTINFO filtered packets that are requested using
+ * ReadFile instead of WSARecv.  If the packet does not match
+ * for this socket's interface then throw it away.
+ */
+static int accept_read_poll( struct fd *fd )
+{
+#ifdef USE_IP_PKTINFO_FILTER
+    struct sock *sock = get_fd_user( fd );
+    unsigned int check_iface, optlen;
+    int handle = get_unix_fd ( fd );
+    struct msghdr hdr;
+    char pktbuf[512];
+    int ret = TRUE;
+
+    memset(&hdr, 0, sizeof(struct msghdr));
+    hdr.msg_control = pktbuf;
+    hdr.msg_controllen = sizeof(pktbuf);
+    hdr.msg_flags = 0;
+
+    check_iface = 0;
+    optlen = sizeof(check_iface);
+    getsockopt(handle, IPPROTO_IP, IP_PKTINFO, (void *) &check_iface, &optlen);
+    if (check_iface)
+    {
+        sem_wait(sock->iface_lock);
+        /* Peek at the packet to check its interface */
+        if ( recvmsg(handle, &hdr, MSG_PEEK|MSG_DONTWAIT) != -1 )
+        {
+            int iface_index = sock->iface_index;
+            struct in_pktinfo *pktinfo = NULL;
+            struct cmsghdr *cmsg;
+
+            for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
+                 cmsg = CMSG_NXTHDR(&hdr, cmsg))
+            {
+                if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
+                    pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+            }
+            if (pktinfo && pktinfo->ipi_ifindex != iface_index)
+            {
+                if ( recvmsg(handle, &hdr, MSG_DONTWAIT) != -1 )
+                    ret = FALSE;
+                else
+                    fprintf(stderr, "Failed to throw away packet with interface mismatch! %m\n");
+            }
+        }
+        else
+            fprintf(stderr, "Failed to peek at message header! %m\n");
+        sem_post(sock->iface_lock);
+    }
+    return ret;
+#else
+    return TRUE;
+#endif /* USE_IP_PKTINFO_FILTER */
+}
+
 static void sock_poll_event( struct fd *fd, int event )
 {
     struct sock *sock = get_fd_user( fd );
@@ -437,6 +504,12 @@ static void sock_poll_event( struct fd *fd, int event )
                 }
             }
         }
+        else if (event & POLLIN) /* POLLIN for non-stream socket */
+        {
+            /* Check to see if the packet is not destined for the given interface */
+            if (!accept_read_poll(fd))
+                event &= ~POLLIN;
+        }
 
         if ( (hangup_seen || event & (POLLHUP|POLLERR)) && (sock->state & (FD_READ|FD_WRITE)) )
         {
@@ -596,8 +669,23 @@ static void sock_destroy( struct object *obj )
     if (sock->event) release_object( sock->event );
     if (sock->fd)
     {
+        int sockfd = get_unix_fd(sock->fd);
+
+        if (sock->iface_lock)
+        {
+            char lockname[100];
+            struct stat fdinfo;
+
+            if (fstat(sockfd, &fdinfo) == 0)
+            {
+                snprintf( lockname, sizeof(lockname), "/wine-ifacelock-%d.%d-%ld",
+                    major(fdinfo.st_dev), minor(fdinfo.st_dev), (long)fdinfo.st_ino );
+                sem_close( sock->iface_lock );
+                sem_unlink( lockname );
+            }
+        }
         /* shut the socket down to force pending poll() calls in the client to return */
-        shutdown( get_unix_fd(sock->fd), SHUT_RDWR );
+        shutdown( sockfd, SHUT_RDWR );
         release_object( sock->fd );
     }
 }
@@ -619,6 +707,8 @@ static void init_sock(struct sock *sock)
     sock->deferred = NULL;
     sock->read_q  = NULL;
     sock->write_q = NULL;
+    sock->iface_index = 0;
+    sock->iface_lock = NULL;
     memset( sock->errors, 0, sizeof(sock->errors) );
 }
 
@@ -962,6 +1052,45 @@ DECL_HANDLER(accept_into_socket)
     release_object( sock );
 }
 
+/* set socket interface */
+DECL_HANDLER(set_socket_iface)
+{
+    struct sock *sock;
+    char lockname[100];
+    struct stat fdinfo;
+    int sockfd;
+
+    if (!(sock = (struct sock *)get_handle_obj( current->process, req->handle,
+                                                FILE_WRITE_ATTRIBUTES, &sock_ops))) return;
+    sock->iface_index = req->iface;
+    sockfd = get_unix_fd(sock->fd);
+    if (fstat(sockfd, &fdinfo) == 0)
+    {
+        snprintf( lockname, sizeof(lockname), "/wine-ifacelock-%d.%d-%ld",
+            major(fdinfo.st_dev), minor(fdinfo.st_dev), (long)fdinfo.st_ino );
+        sock->iface_lock = sem_open( lockname, O_CREAT, S_IRUSR | S_IWUSR, 1 );
+    }
+    else
+        fprintf(stderr, "failed to create socket semaphore\n");
+    release_object( &sock->obj );
+}
+
+/* get socket interface */
+DECL_HANDLER(get_socket_iface)
+{
+    struct sock *sock;
+
+    sock = (struct sock *)get_handle_obj( current->process, req->handle, FILE_READ_ATTRIBUTES, &sock_ops );
+    if (!sock)
+    {
+        reply->iface = 0;
+        set_error( WSAENOTSOCK );
+        return;
+    }
+    reply->iface = sock->iface_index;
+    release_object( &sock->obj );
+}
+
 /* set socket event parameters */
 DECL_HANDLER(set_socket_event)
 {
diff --git a/server/trace.c b/server/trace.c
index 6f0e01e..9cc24fe 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1569,6 +1569,22 @@ static void dump_get_socket_event_reply( const struct get_socket_event_reply *re
     dump_varargs_ints( ", errors=", cur_size );
 }
 
+static void dump_set_socket_iface_request( const struct set_socket_iface_request *req )
+{
+    fprintf( stderr, " handle=%04x", req->handle );
+    fprintf( stderr, ", iface=%d", req->iface );
+}
+
+static void dump_get_socket_iface_request( const struct get_socket_iface_request *req )
+{
+    fprintf( stderr, " handle=%04x", req->handle );
+}
+
+static void dump_get_socket_iface_reply( const struct get_socket_iface_reply *req )
+{
+    fprintf( stderr, " iface=%d", req->iface );
+}
+
 static void dump_enable_socket_event_request( const struct enable_socket_event_request *req )
 {
     fprintf( stderr, " handle=%04x", req->handle );
@@ -3887,6 +3903,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_accept_into_socket_request,
     (dump_func)dump_set_socket_event_request,
     (dump_func)dump_get_socket_event_request,
+    (dump_func)dump_set_socket_iface_request,
+    (dump_func)dump_get_socket_iface_request,
     (dump_func)dump_enable_socket_event_request,
     (dump_func)dump_set_socket_deferred_request,
     (dump_func)dump_alloc_console_request,
@@ -4134,6 +4152,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     NULL,
     (dump_func)dump_get_socket_event_reply,
     NULL,
+    (dump_func)dump_get_socket_iface_reply,
+    NULL,
     NULL,
     (dump_func)dump_alloc_console_reply,
     NULL,
@@ -4379,6 +4399,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "accept_into_socket",
     "set_socket_event",
     "get_socket_event",
+    "set_socket_iface",
+    "get_socket_iface",
     "enable_socket_event",
     "set_socket_deferred",
     "alloc_console",
-- 
1.7.0.4

