Zebediah Figura : server: Introduce IOCTL_AFD_WINE_CONNECT.

Alexandre Julliard julliard at winehq.org
Tue May 18 15:42:37 CDT 2021


Module: wine
Branch: master
Commit: 4256253419998b433ef3143603c32798dc3fccb3
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=4256253419998b433ef3143603c32798dc3fccb3

Author: Zebediah Figura <z.figura12 at gmail.com>
Date:   Mon May 17 21:29:59 2021 -0500

server: Introduce IOCTL_AFD_WINE_CONNECT.

I was able to reverse-engineer a native CONNECT ioctl, with code 0x801; but I
was not able to find a valid set of parameters which would allow us to
implement either connect() or ConnectEx(). In particular, I could not find a
way to make the ioctl respect nonblocking mode, and I could not find a way to
specify an initial buffer to be sent.

Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 include/wine/afd.h |   9 ++++
 server/sock.c      | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 161 insertions(+)

diff --git a/include/wine/afd.h b/include/wine/afd.h
index e890020ca88..b085981b6c0 100644
--- a/include/wine/afd.h
+++ b/include/wine/afd.h
@@ -36,6 +36,7 @@ struct afd_listen_params
 #define IOCTL_AFD_WINE_CREATE               CTL_CODE(FILE_DEVICE_NETWORK, 200, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define IOCTL_AFD_WINE_ACCEPT               CTL_CODE(FILE_DEVICE_NETWORK, 201, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define IOCTL_AFD_WINE_ACCEPT_INTO          CTL_CODE(FILE_DEVICE_NETWORK, 202, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_AFD_WINE_CONNECT              CTL_CODE(FILE_DEVICE_NETWORK, 203, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 #define IOCTL_AFD_WINE_ADDRESS_LIST_CHANGE  CTL_CODE(FILE_DEVICE_NETWORK, 323, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
@@ -51,4 +52,12 @@ struct afd_accept_into_params
     unsigned int recv_len, local_len;
 };
 
+struct afd_connect_params
+{
+    int addr_len;
+    int synchronous;
+    /* VARARG(addr, struct sockaddr, addr_len); */
+    /* VARARG(data, bytes); */
+};
+
 #endif
diff --git a/server/sock.c b/server/sock.c
index 565fb4c5a2c..9896810def1 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -106,6 +106,14 @@ struct accept_req
     unsigned int recv_len, local_len;
 };
 
+struct connect_req
+{
+    struct async *async;
+    struct iosb *iosb;
+    struct sock *sock;
+    unsigned int addr_len, send_len, send_cursor;
+};
+
 struct sock
 {
     struct object       obj;         /* object header */
@@ -141,10 +149,12 @@ struct sock
     struct async_queue  write_q;     /* queue for asynchronous writes */
     struct async_queue  ifchange_q;  /* queue for interface change notifications */
     struct async_queue  accept_q;    /* queue for asynchronous accepts */
+    struct async_queue  connect_q;   /* queue for asynchronous connects */
     struct object      *ifchange_obj; /* the interface change notification object */
     struct list         ifchange_entry; /* entry in ifchange notification list */
     struct list         accept_list; /* list of pending accept requests */
     struct accept_req  *accept_recv_req; /* pending accept-into request which will recv on this socket */
+    struct connect_req *connect_req; /* pending connection request */
 };
 
 static void sock_dump( struct object *obj, int verbose );
@@ -558,6 +568,58 @@ static void complete_async_accept_recv( struct accept_req *req )
     fill_accept_output( req );
 }
 
+static void free_connect_req( void *private )
+{
+    struct connect_req *req = private;
+
+    req->sock->connect_req = NULL;
+    release_object( req->async );
+    release_object( req->iosb );
+    release_object( req->sock );
+    free( req );
+}
+
+static void complete_async_connect( struct sock *sock )
+{
+    struct connect_req *req = sock->connect_req;
+    const char *in_buffer;
+    struct iosb *iosb;
+    size_t len;
+    int ret;
+
+    if (debug_level) fprintf( stderr, "completing connect request for socket %p\n", sock );
+
+    sock->pending_events &= ~(FD_CONNECT | FD_READ | FD_WRITE);
+    sock->reported_events &= ~(FD_CONNECT | FD_READ | FD_WRITE);
+    sock->state |= FD_WINE_CONNECTED;
+    sock->state &= ~(FD_CONNECT | FD_WINE_LISTENING);
+
+    if (!req->send_len)
+    {
+        set_error( STATUS_SUCCESS );
+        return;
+    }
+
+    iosb = req->iosb;
+    in_buffer = (const char *)iosb->in_data + sizeof(struct afd_connect_params) + req->addr_len;
+    len = req->send_len - req->send_cursor;
+
+    ret = send( get_unix_fd( sock->fd ), in_buffer + req->send_cursor, len, 0 );
+    if (ret < 0 && errno != EWOULDBLOCK)
+        set_error( sock_get_ntstatus( errno ) );
+    else if (ret == len)
+    {
+        iosb->result = req->send_len;
+        iosb->status = STATUS_SUCCESS;
+        set_error( STATUS_ALERTED );
+    }
+    else
+    {
+        req->send_cursor += ret;
+        set_error( STATUS_PENDING );
+    }
+}
+
 static int sock_dispatch_asyncs( struct sock *sock, int event, int error )
 {
     if (event & (POLLIN | POLLPRI))
@@ -583,6 +645,13 @@ static int sock_dispatch_asyncs( struct sock *sock, int event, int error )
         }
     }
 
+    if ((event & POLLOUT) && sock->connect_req && sock->connect_req->iosb->status == STATUS_PENDING)
+    {
+        complete_async_connect( sock );
+        if (get_error() != STATUS_PENDING)
+            async_terminate( sock->connect_req->async, get_error() );
+    }
+
     if (is_fd_overlapped( sock->fd ))
     {
         if (event & (POLLIN|POLLPRI) && async_waiting( &sock->read_q ))
@@ -617,6 +686,9 @@ static int sock_dispatch_asyncs( struct sock *sock, int event, int error )
 
         if (sock->accept_recv_req && sock->accept_recv_req->iosb->status == STATUS_PENDING)
             async_terminate( sock->accept_recv_req->async, status );
+
+        if (sock->connect_req)
+            async_terminate( sock->connect_req->async, status );
     }
 
     return event;
@@ -865,6 +937,9 @@ static int sock_close_handle( struct object *obj, struct process *process, obj_h
 
         LIST_FOR_EACH_ENTRY_SAFE( req, next, &sock->accept_list, struct accept_req, entry )
             async_terminate( req->async, STATUS_CANCELLED );
+
+        if (sock->connect_req)
+            async_terminate( sock->connect_req->async, STATUS_CANCELLED );
     }
 
     return 1;
@@ -887,6 +962,7 @@ static void sock_destroy( struct object *obj )
     free_async_queue( &sock->write_q );
     free_async_queue( &sock->ifchange_q );
     free_async_queue( &sock->accept_q );
+    free_async_queue( &sock->connect_q );
     if (sock->event) release_object( sock->event );
     if (sock->fd)
     {
@@ -919,10 +995,12 @@ static struct sock *create_socket(void)
     sock->deferred = NULL;
     sock->ifchange_obj = NULL;
     sock->accept_recv_req = NULL;
+    sock->connect_req = NULL;
     init_async_queue( &sock->read_q );
     init_async_queue( &sock->write_q );
     init_async_queue( &sock->ifchange_q );
     init_async_queue( &sock->accept_q );
+    init_async_queue( &sock->connect_q );
     memset( sock->errors, 0, sizeof(sock->errors) );
     list_init( &sock->accept_list );
     return sock;
@@ -1318,6 +1396,7 @@ static int sock_get_ntstatus( int err )
         case EPIPE:
         case ECONNRESET:        return STATUS_CONNECTION_RESET;
         case ECONNABORTED:      return STATUS_CONNECTION_ABORTED;
+        case EISCONN:           return STATUS_CONNECTION_ACTIVE;
 
         case 0:                 return STATUS_SUCCESS;
         default:
@@ -1486,6 +1565,79 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
         return 0;
     }
 
+    case IOCTL_AFD_WINE_CONNECT:
+    {
+        const struct afd_connect_params *params = get_req_data();
+        const struct sockaddr *addr;
+        struct connect_req *req;
+        int send_len, ret;
+
+        if (get_req_data_size() < sizeof(*params) ||
+            get_req_data_size() - sizeof(*params) < params->addr_len)
+        {
+            set_error( STATUS_BUFFER_TOO_SMALL );
+            return 0;
+        }
+        send_len = get_req_data_size() - sizeof(*params) - params->addr_len;
+        addr = (const struct sockaddr *)(params + 1);
+
+        if (sock->accept_recv_req)
+        {
+            set_error( STATUS_INVALID_PARAMETER );
+            return 0;
+        }
+
+        if (sock->connect_req)
+        {
+            set_error( params->synchronous ? STATUS_INVALID_PARAMETER : STATUS_CONNECTION_ACTIVE );
+            return 0;
+        }
+
+        ret = connect( unix_fd, addr, params->addr_len );
+        if (ret < 0 && errno != EINPROGRESS)
+        {
+            set_error( sock_get_ntstatus( errno ) );
+            return 0;
+        }
+
+        sock->pending_events &= ~(FD_CONNECT | FD_READ | FD_WRITE);
+        sock->reported_events &= ~(FD_CONNECT | FD_READ | FD_WRITE);
+
+        if (!ret)
+        {
+            sock->state |= FD_WINE_CONNECTED | FD_READ | FD_WRITE;
+            sock->state &= ~FD_CONNECT;
+
+            if (!send_len) return 1;
+        }
+
+        if (!(req = mem_alloc( sizeof(*req) )))
+            return 0;
+
+        sock->state |= FD_CONNECT;
+
+        if (params->synchronous && (sock->state & FD_WINE_NONBLOCKING))
+        {
+            sock_reselect( sock );
+            set_error( STATUS_DEVICE_NOT_READY );
+            return 0;
+        }
+
+        req->async = (struct async *)grab_object( async );
+        req->iosb = async_get_iosb( async );
+        req->sock = (struct sock *)grab_object( sock );
+        req->addr_len = params->addr_len;
+        req->send_len = send_len;
+        req->send_cursor = 0;
+
+        async_set_completion_callback( async, free_connect_req, req );
+        sock->connect_req = req;
+        queue_async( &sock->connect_q, async );
+        sock_reselect( sock );
+        set_error( STATUS_PENDING );
+        return 1;
+    }
+
     case IOCTL_AFD_WINE_ADDRESS_LIST_CHANGE:
         if ((sock->state & FD_WINE_NONBLOCKING) && async_is_blocking( async ))
         {




More information about the wine-cvs mailing list