From: Rose Hellsing <rose@pinkro.se> Add support for clients to connect to LPC ports and servers to listen for connection requests: - Add lpc_message structure for internal message handling - Add global_pending_connects list to track pending connections - Add connect_lpc_port handler to create client ports and queue connection requests - Add listen_lpc_port handler to retrieve pending connection requests The connection flow works as follows: 1. Client calls connect_lpc_port which creates a client port and queues a connection request message on the server port 2. Server calls listen_lpc_port to receive connection request details 3. Server will accept/reject with accept_lpc_connect (next commit) --- server/lpc_port.c | 271 +++++++++++++++++++++++++++++++++++++++++++- server/protocol.def | 26 +++++ 2 files changed, 295 insertions(+), 2 deletions(-) diff --git a/server/lpc_port.c b/server/lpc_port.c index 3c4ef23a4fb..a6f2649c424 100644 --- a/server/lpc_port.c +++ b/server/lpc_port.c @@ -44,12 +44,23 @@ /* Port types */ #define PORT_TYPE_SERVER 0x01 /* Named port that server listens on */ +#define PORT_TYPE_CLIENT 0x02 /* Client's end of connection */ /* Port flags */ #define PORT_FLAG_WAITABLE 0x0001 +/* LPC message types */ +#define LPC_CONNECTION_REQUEST 10 + /* Maximum message size */ #define MAX_LPC_MESSAGE_SIZE 0x40000 +#define MAX_LPC_DATA_SIZE 0x100000 + +/* Global counter for generating unique message IDs */ +static unsigned int global_msg_id_counter = 0; + +/* Global list of pending connection requests */ +static struct list global_pending_connects = LIST_INIT(global_pending_connects); static const WCHAR lpc_port_name[] = {'L','P','C',' ','P','o','r','t'}; @@ -65,18 +76,40 @@ struct type_descr lpc_port_type = }, }; +/* Internal message structure */ +struct lpc_message +{ + struct list entry; /* queue entry */ + struct list global_entry; /* entry in global_pending_connects */ + struct lpc_port *sender_port; /* port that sent this message */ + struct lpc_port *server_port; /* server port (for connection requests) */ + struct thread *sender_thread; /* thread that sent this message */ + unsigned int msg_id; /* unique message ID */ + unsigned int msg_type; /* LPC message type */ + process_id_t client_pid; /* sender's process ID */ + thread_id_t client_tid; /* sender's thread ID */ + data_size_t data_size; /* size of message data */ + char data[1]; /* variable-length message data */ +}; + /* LPC port object */ struct lpc_port { struct object obj; /* object header */ unsigned int port_type; /* PORT_TYPE_* */ unsigned int flags; /* PORT_FLAG_* */ + struct lpc_port *connection_port; /* reference to connection port */ + struct lpc_port *connected_port; /* paired port: client <-> channel */ struct list msg_queue; /* list of pending messages */ + struct list pending_connects;/* list of pending connection messages */ struct object *queue_event; /* event signaled when message arrives */ unsigned int max_msg_len; /* maximum message length */ unsigned int max_connect_info;/* maximum connection info length */ struct object *wait_event; /* event for WaitForSingleObject (waitable ports) */ struct process *server_process; /* server process (for named ports) */ + struct thread *client_thread; /* client thread (for NtCompleteConnectPort) */ + struct object *connect_event; /* event signaled when connection completes */ + unsigned int connect_status; /* STATUS_SUCCESS or error code from accept */ }; static void lpc_port_dump( struct object *obj, int verbose ); @@ -108,6 +141,58 @@ static const struct object_ops lpc_port_ops = lpc_port_destroy /* destroy */ }; +/* Allocate a new message with the given data size */ +static struct lpc_message *alloc_lpc_message( data_size_t data_size ) +{ + struct lpc_message *msg; + data_size_t alloc_size; + + if (data_size > MAX_LPC_DATA_SIZE) + { + set_error( STATUS_INVALID_PARAMETER ); + return NULL; + } + + alloc_size = max( sizeof(struct lpc_message), offsetof(struct lpc_message, data) + data_size ); + msg = mem_alloc( alloc_size ); + if (msg) + { + memset( msg, 0, alloc_size ); + list_init( &msg->global_entry ); + msg->data_size = data_size; + } + return msg; +} + +/* Free a message */ +static void free_lpc_message( struct lpc_message *msg ) +{ + if (msg) + { + if (!list_empty( &msg->global_entry )) + list_remove( &msg->global_entry ); + if (msg->sender_port) release_object( msg->sender_port ); + if (msg->server_port) release_object( msg->server_port ); + if (msg->sender_thread) release_object( msg->sender_thread ); + free( msg ); + } +} + +/* Get the next globally unique message ID */ +static unsigned int get_next_msg_id( void ) +{ + return ++global_msg_id_counter; +} + +/* Signal the port's queue event to wake waiting threads */ +static void signal_port_queue( struct lpc_port *port ) +{ + if (port->queue_event) + signal_sync( port->queue_event ); + if (port->wait_event) + signal_sync( port->wait_event ); +} + static struct lpc_port *create_lpc_port( struct object *root, const struct unicode_str *name, unsigned int attr, unsigned int flags, unsigned int max_msg_len, unsigned int max_connect_info, @@ -126,12 +211,18 @@ static struct lpc_port *create_lpc_port( struct object *root, const struct unico { port->port_type = PORT_TYPE_SERVER; port->flags = flags; + port->connection_port = (struct lpc_port *)grab_object( port ); + port->connected_port = NULL; list_init( &port->msg_queue ); + list_init( &port->pending_connects ); port->queue_event = create_internal_sync( 0, 0 ); port->max_msg_len = max_msg_len ? max_msg_len : MAX_LPC_MESSAGE_SIZE; port->max_connect_info = max_connect_info ? max_connect_info : 256; port->wait_event = NULL; port->server_process = (struct process *)grab_object( current->process ); + port->client_thread = NULL; + port->connect_event = NULL; + port->connect_status = STATUS_PENDING; if (!port->queue_event) { @@ -153,19 +244,67 @@ static struct lpc_port *create_lpc_port( struct object *root, const struct unico return port; } +/* Create a client port (internal, no name) */ +static struct lpc_port *create_port_internal( unsigned int port_type, struct lpc_port *connection_port ) +{ + struct lpc_port *port; + + port = alloc_object( &lpc_port_ops ); + if (!port) return NULL; + + port->port_type = port_type; + port->flags = 0; + port->connection_port = (struct lpc_port *)grab_object( connection_port ); + port->connected_port = NULL; + list_init( &port->msg_queue ); + list_init( &port->pending_connects ); + port->queue_event = create_internal_sync( 0, 0 ); + port->max_msg_len = connection_port->max_msg_len; + port->max_connect_info = connection_port->max_connect_info; + port->wait_event = NULL; + port->server_process = NULL; + port->client_thread = NULL; + port->connect_event = NULL; + port->connect_status = STATUS_PENDING; + + if (port_type == PORT_TYPE_CLIENT) + { + port->connect_event = (struct object *)create_server_internal_sync( 1, 0 ); + if (!port->connect_event) + { + release_object( port ); + return NULL; + } + } + + if (!port->queue_event) + { + release_object( port ); + return NULL; + } + + return port; +} + static void lpc_port_dump( struct object *obj, int verbose ) { struct lpc_port *port = (struct lpc_port *)obj; + static const char *type_names[] = { "???", "SERVER", "CLIENT", "CHANNEL" }; + const char *type_name = port->port_type < 4 ? type_names[port->port_type] : "???"; assert( obj->ops == &lpc_port_ops ); fprintf( stderr, "LPC Port type=%s flags=%04x max_msg=%u max_connect=%u\n", - port->port_type == PORT_TYPE_SERVER ? "SERVER" : "?", - port->flags, port->max_msg_len, port->max_connect_info ); + type_name, port->flags, port->max_msg_len, port->max_connect_info ); } static struct object *lpc_port_get_sync( struct object *obj ) { struct lpc_port *port = (struct lpc_port *)obj; + assert( obj->ops == &lpc_port_ops ); + + /* For clients with a connect_event, return it for connection wait */ + if (port->port_type == PORT_TYPE_CLIENT && port->connect_event) + return grab_object( port->connect_event ); if (port->wait_event) return grab_object( port->wait_event ); @@ -179,12 +318,31 @@ static struct object *lpc_port_get_sync( struct object *obj ) static void lpc_port_destroy( struct object *obj ) { struct lpc_port *port = (struct lpc_port *)obj; + struct lpc_message *msg, *next_msg; assert( obj->ops == &lpc_port_ops ); + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->msg_queue, struct lpc_message, entry ) + { + list_remove( &msg->entry ); + free_lpc_message( msg ); + } + + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->pending_connects, struct lpc_message, entry ) + { + list_remove( &msg->entry ); + free_lpc_message( msg ); + } + if (port->queue_event) release_object( port->queue_event ); if (port->wait_event) release_object( port->wait_event ); + if (port->connect_event) release_object( port->connect_event ); + if (port->connection_port && port->connection_port != port) + release_object( port->connection_port ); + if (port->connected_port && port->connected_port != port) + release_object( port->connected_port ); if (port->server_process) release_object( port->server_process ); + if (port->client_thread) release_object( port->client_thread ); } /* Create an LPC port */ @@ -211,3 +369,112 @@ DECL_HANDLER(create_lpc_port) if (root) release_object( root ); } + +/* Connect to an LPC port */ +DECL_HANDLER(connect_lpc_port) +{ + struct lpc_port *connection_port; + struct lpc_port *client_port; + struct lpc_message *msg; + struct unicode_str name; + struct object *root; + const struct security_descriptor *sd; + const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); + data_size_t info_size = req->info_size; + + if (!objattr) return; + + connection_port = (struct lpc_port *)open_named_object( root, &lpc_port_ops, &name, objattr->attributes ); + if (root) release_object( root ); + + if (!connection_port) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + return; + } + + if (connection_port->port_type != PORT_TYPE_SERVER) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( connection_port ); + return; + } + + /* Create client port */ + client_port = create_port_internal( PORT_TYPE_CLIENT, connection_port ); + if (!client_port) + { + release_object( connection_port ); + return; + } + + /* Create connection request message */ + msg = alloc_lpc_message( info_size ); + if (!msg) + { + release_object( client_port ); + release_object( connection_port ); + return; + } + + msg->msg_id = get_next_msg_id(); + msg->msg_type = LPC_CONNECTION_REQUEST; + msg->sender_port = (struct lpc_port *)grab_object( client_port ); + msg->server_port = (struct lpc_port *)grab_object( connection_port ); + msg->sender_thread = (struct thread *)grab_object( current ); + msg->client_pid = current->process->id; + msg->client_tid = current->id; + if (info_size) memcpy( msg->data, get_req_data(), info_size ); + + /* Queue on server's pending_connects list and global list */ + list_add_tail( &connection_port->pending_connects, &msg->entry ); + list_add_tail( &global_pending_connects, &msg->global_entry ); + + /* Signal server that connection request is pending */ + signal_port_queue( connection_port ); + + /* Return handle to client port */ + reply->handle = alloc_handle_no_access_check( current->process, client_port, + req->access, objattr->attributes ); + release_object( client_port ); + release_object( connection_port ); +} + +/* Listen for connection requests */ +DECL_HANDLER(listen_lpc_port) +{ + struct lpc_port *port; + struct lpc_message *msg; + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + 0, &lpc_port_ops ); + if (!port) return; + + if (port->port_type != PORT_TYPE_SERVER) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + /* Get first pending connection request */ + if (list_empty( &port->pending_connects )) + { + set_error( STATUS_PENDING ); + release_object( port ); + return; + } + + msg = LIST_ENTRY( list_head( &port->pending_connects ), struct lpc_message, entry ); + + reply->msg_size = msg->data_size; + reply->client_pid = msg->client_pid; + reply->client_tid = msg->client_tid; + reply->msg_id = msg->msg_id; + reply->message = 0; + + if (msg->data_size) + set_reply_data( msg->data, min( msg->data_size, get_reply_max_size() ) ); + + release_object( port ); +} diff --git a/server/protocol.def b/server/protocol.def index af8011611b3..5efd5d54cca 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4286,3 +4286,29 @@ enum inproc_sync_type @REPLY obj_handle_t handle; /* port handle */ @END + + +/* Connect to an LPC port */ +@REQ(connect_lpc_port) + unsigned int access; /* desired access rights */ + data_size_t info_size; /* size of connection info */ + VARARG(objattr,object_attributes);/* object attributes */ + VARARG(info,bytes); /* connection info data */ +@REPLY + obj_handle_t handle; /* handle to client port */ + data_size_t info_size; /* size of returned info */ + VARARG(info,bytes); /* returned connection info */ +@END + + +/* Listen for connection requests */ +@REQ(listen_lpc_port) + obj_handle_t handle; /* handle to server port */ +@REPLY + client_ptr_t message; /* message pointer (unused) */ + data_size_t msg_size; /* size of connection info */ + process_id_t client_pid; /* client process ID */ + thread_id_t client_tid; /* client thread ID */ + unsigned int msg_id; /* message ID for accept */ + VARARG(info,bytes); /* connection info data */ +@END -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611