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@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2021-05-17 21:29:59 -05:00 committed by Alexandre Julliard
parent 737fe1f993
commit 4256253419
2 changed files with 161 additions and 0 deletions

View File

@ -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

View File

@ -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 ))
{