nsiproxy: Implement UDP table on top of a server call.

This commit is contained in:
Tim Clem 2024-08-28 08:47:34 -07:00 committed by Alexandre Julliard
parent 9085bc7b87
commit a3f737f614
Notes: Alexandre Julliard 2024-11-08 21:36:34 +01:00
Approved-by: Huw Davies (@huw)
Approved-by: Elizabeth Figura (@zfigura)
Approved-by: Alex Henrie (@alexhenrie)
Approved-by: Alexandre Julliard (@julliard)
Merge-Request: https://gitlab.winehq.org/wine/wine/merge_requests/6399
7 changed files with 236 additions and 192 deletions

View file

@ -203,231 +203,83 @@ static NTSTATUS udp_stats_get_all_parameters( const void *key, UINT key_size, vo
return STATUS_NOT_SUPPORTED;
}
#ifdef __APPLE__
static int pcblist_mib[CTL_MAXNAME];
static size_t pcblist_mib_len = CTL_MAXNAME;
static void init_pcblist64_mib( void )
{
sysctlnametomib( "net.inet.udp.pcblist64", pcblist_mib, &pcblist_mib_len );
}
#endif
static NTSTATUS udp_endpoint_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size,
void *dynamic_data, UINT dynamic_size,
void *static_data, UINT static_size, UINT_PTR *count )
{
UINT num = 0;
NTSTATUS status = STATUS_SUCCESS;
BOOL want_data = key_size || rw_size || dynamic_size || static_size;
struct nsi_udp_endpoint_key key, *key_out = key_data;
struct nsi_udp_endpoint_static stat, *stat_out = static_data;
struct ipv6_addr_scope *addr_scopes = NULL;
unsigned int addr_scopes_size = 0, pid_map_size = 0;
struct pid_map *pid_map = NULL;
unsigned int addr_scopes_size = 0;
NTSTATUS ret = STATUS_SUCCESS;
udp_endpoint *endpoints = NULL;
TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data, key_size, rw_data, rw_size,
dynamic_data, dynamic_size, static_data, static_size, count );
#ifdef __linux__
if (want_data)
{
FILE *fp;
char buf[512], *ptr;
int inode;
UINT addr;
endpoints = malloc( sizeof(*endpoints) * (*count) );
if (!endpoints) return STATUS_NO_MEMORY;
}
if (!(fp = fopen( "/proc/net/udp", "r" ))) return ERROR_NOT_SUPPORTED;
memset( &key, 0, sizeof(key) );
memset( &stat, 0, sizeof(stat) );
if (stat_out) pid_map = get_pid_map( &pid_map_size );
/* skip header line */
ptr = fgets( buf, sizeof(buf), fp );
while ((ptr = fgets( buf, sizeof(buf), fp )))
SERVER_START_REQ( get_udp_endpoints )
{
wine_server_set_reply( req, endpoints, want_data ? (sizeof(*endpoints) * (*count)) : 0 );
if (!(ret = wine_server_call( req )))
*count = reply->count;
else if (ret == STATUS_BUFFER_TOO_SMALL)
{
if (sscanf( ptr, "%*u: %x:%hx %*s %*s %*s %*s %*s %*s %*s %d",
&addr, &key.local.Ipv4.sin_port, &inode ) != 3)
continue;
key.local.Ipv4.sin_family = WS_AF_INET;
key.local.Ipv4.sin_addr.WS_s_addr = addr;
key.local.Ipv4.sin_port = htons( key.local.Ipv4.sin_port );
if (stat_out)
*count = reply->count;
if (want_data)
{
stat.pid = find_owning_pid( pid_map, pid_map_size, inode );
stat.create_time = 0; /* FIXME */
stat.flags = 0; /* FIXME */
stat.mod_info = 0; /* FIXME */
free( endpoints );
return STATUS_BUFFER_OVERFLOW;
}
if (num < *count)
{
if (key_out) *key_out++ = key;
if (stat_out) *stat_out++ = stat;
}
num++;
}
fclose( fp );
if ((fp = fopen( "/proc/net/udp6", "r" )))
{
memset( &key, 0, sizeof(key) );
memset( &stat, 0, sizeof(stat) );
addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size );
/* skip header line */
ptr = fgets( buf, sizeof(buf), fp );
while ((ptr = fgets( buf, sizeof(buf), fp )))
{
UINT *local_addr = (UINT *)&key.local.Ipv6.sin6_addr;
if (sscanf( ptr, "%*u: %8x%8x%8x%8x:%hx %*s %*s %*s %*s %*s %*s %*s %d",
local_addr, local_addr + 1, local_addr + 2, local_addr + 3,
&key.local.Ipv6.sin6_port, &inode ) != 6)
continue;
key.local.Ipv6.sin6_family = WS_AF_INET6;
key.local.Ipv6.sin6_port = htons( key.local.Ipv6.sin6_port );
key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes,
addr_scopes_size );
if (stat_out)
{
stat.pid = find_owning_pid( pid_map, pid_map_size, inode );
stat.create_time = 0; /* FIXME */
stat.flags = 0; /* FIXME */
stat.mod_info = 0; /* FIXME */
}
if (num < *count)
{
if (key_out) *key_out++ = key;
if (stat_out) *stat_out++ = stat;
}
num++;
}
fclose( fp );
else return STATUS_SUCCESS;
}
}
#elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_PCBLIST) && defined(HAVE_STRUCT_XINPGEN)
SERVER_END_REQ;
addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size );
for (unsigned int i = 0; i < *count; i++)
{
size_t len = 0;
char *buf = NULL;
struct xinpgen *xig, *orig_xig;
udp_endpoint *endpt = &endpoints[i];
#ifdef __APPLE__
static pthread_once_t mib_init_once = PTHREAD_ONCE_INIT;
pthread_once( &mib_init_once, init_pcblist64_mib );
#else
int pcblist_mib[] = { CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_PCBLIST };
size_t pcblist_mib_len = ARRAY_SIZE(pcblist_mib);
#endif
if (sysctl( pcblist_mib, pcblist_mib_len, NULL, &len, NULL, 0 ) < 0)
if (key_out)
{
ERR( "Failure to read net.inet.udp.pcblist via sysctl!\n" );
status = STATUS_NOT_SUPPORTED;
goto err;
}
buf = malloc( len );
if (!buf)
{
status = STATUS_NO_MEMORY;
goto err;
}
if (sysctl( pcblist_mib, pcblist_mib_len, buf, &len, NULL, 0 ) < 0)
{
ERR( "Failure to read net.inet.udp.pcblist via sysctl!\n" );
status = STATUS_NOT_SUPPORTED;
goto err;
}
/* Might be nothing here; first entry is just a header it seems */
if (len <= sizeof(struct xinpgen)) goto err;
addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size );
if (stat_out) pid_map = get_pid_map( &pid_map_size );
orig_xig = (struct xinpgen *)buf;
xig = orig_xig;
for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);
xig->xig_len > sizeof (struct xinpgen);
xig = (struct xinpgen *)((char *)xig + xig->xig_len))
{
#ifdef __APPLE__
struct xinpcb64 *in = (struct xinpcb64 *)xig;
struct xsocket64 *sock = &in->xi_socket;
#elif __FreeBSD_version >= 1200026
struct xinpcb *in = (struct xinpcb *)xig;
struct xsocket *sock = &in->xi_socket;
#else
struct inpcb *in = &((struct xinpcb *)xig)->xi_inp;
struct xsocket *sock = &((struct xinpcb *)xig)->xi_socket;
#endif
static const struct in6_addr zero;
/* Ignore sockets for other protocols */
if (sock->xso_protocol != IPPROTO_UDP) continue;
/* Ignore PCBs that were freed while generating the data */
if (in->inp_gencnt > orig_xig->xig_gen) continue;
/* we're only interested in IPv4 and IPv6 addresses */
if (!(in->inp_vflag & (INP_IPV4 | INP_IPV6))) continue;
/* If all 0's, skip it */
if (in->inp_vflag & INP_IPV4 && !in->inp_laddr.s_addr) continue;
if (in->inp_vflag & INP_IPV6 && !memcmp( &in->in6p_laddr, &zero, sizeof(zero) ) && !in->inp_lport) continue;
if (in->inp_vflag & INP_IPV4)
memset( &key, 0, sizeof(key) );
if (endpt->common.family == WS_AF_INET)
{
key.local.Ipv4.sin_family = WS_AF_INET;
key.local.Ipv4.sin_addr.WS_s_addr = in->inp_laddr.s_addr;
key.local.Ipv4.sin_port = in->inp_lport;
key.local.Ipv4.sin_addr.WS_s_addr = endpt->ipv4.addr;
key.local.Ipv4.sin_port = endpt->ipv4.port;
}
else
{
key.local.Ipv6.sin6_family = WS_AF_INET6;
memcpy( &key.local.Ipv6.sin6_addr, &in->in6p_laddr, sizeof(in->in6p_laddr) );
key.local.Ipv6.sin6_port = in->inp_lport;
memcpy( &key.local.Ipv6.sin6_addr, &endpt->ipv6.addr, 16 );
key.local.Ipv6.sin6_port = endpt->ipv6.port;
key.local.Ipv6.sin6_scope_id = find_ipv6_addr_scope( &key.local.Ipv6.sin6_addr, addr_scopes,
addr_scopes_size );
}
if (stat_out)
{
stat.pid = find_owning_pid( pid_map, pid_map_size, (UINT_PTR)sock->so_pcb );
stat.create_time = 0; /* FIXME */
stat.flags = !(in->inp_flags & INP_ANONPORT);
stat.mod_info = 0; /* FIXME */
}
if (num < *count)
{
if (key_out) *key_out++ = key;
if (stat_out) *stat_out++ = stat;
}
num++;
*key_out++ = key;
}
if (stat_out)
{
memset( &stat, 0, sizeof(stat) );
stat.pid = endpt->common.owner;
stat.create_time = 0; /* FIXME */
stat.mod_info = 0; /* FIXME */
*stat_out++ = stat;
}
err:
free( buf );
}
#else
FIXME( "not implemented\n" );
return STATUS_NOT_IMPLEMENTED;
#endif
if (!want_data || num <= *count) *count = num;
else status = STATUS_BUFFER_OVERFLOW;
free( pid_map );
free( addr_scopes );
return status;
free( endpoints );
return STATUS_SUCCESS;
}
static struct module_table udp_tables[] =

View file

@ -4891,6 +4891,45 @@ struct get_tcp_connections_reply
};
typedef union
{
struct
{
unsigned int family;
process_id_t owner;
} common;
struct
{
unsigned int family;
process_id_t owner;
unsigned int addr;
unsigned int port;
} ipv4;
struct
{
unsigned int family;
process_id_t owner;
unsigned char addr[16];
unsigned int scope_id;
unsigned int port;
} ipv6;
} udp_endpoint;
struct get_udp_endpoints_request
{
struct request_header __header;
char __pad_12[4];
};
struct get_udp_endpoints_reply
{
struct reply_header __header;
unsigned int count;
/* VARARG(endpoints,udp_endpoints); */
char __pad_12[4];
};
struct create_mailslot_request
{
@ -6005,6 +6044,7 @@ enum request
REQ_get_security_object,
REQ_get_system_handles,
REQ_get_tcp_connections,
REQ_get_udp_endpoints,
REQ_create_mailslot,
REQ_set_mailslot_info,
REQ_create_directory,
@ -6299,6 +6339,7 @@ union generic_request
struct get_security_object_request get_security_object_request;
struct get_system_handles_request get_system_handles_request;
struct get_tcp_connections_request get_tcp_connections_request;
struct get_udp_endpoints_request get_udp_endpoints_request;
struct create_mailslot_request create_mailslot_request;
struct set_mailslot_info_request set_mailslot_info_request;
struct create_directory_request create_directory_request;
@ -6591,6 +6632,7 @@ union generic_reply
struct get_security_object_reply get_security_object_reply;
struct get_system_handles_reply get_system_handles_reply;
struct get_tcp_connections_reply get_tcp_connections_reply;
struct get_udp_endpoints_reply get_udp_endpoints_reply;
struct create_mailslot_reply create_mailslot_reply;
struct set_mailslot_info_reply set_mailslot_info_reply;
struct create_directory_reply create_directory_reply;
@ -6651,7 +6693,7 @@ union generic_reply
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 839
#define SERVER_PROTOCOL_VERSION 840
/* ### protocol_version end ### */

View file

@ -3471,6 +3471,38 @@ typedef union
@END
typedef union
{
struct
{
unsigned int family;
process_id_t owner;
} common;
struct
{
unsigned int family;
process_id_t owner;
unsigned int addr;
unsigned int port;
} ipv4;
struct
{
unsigned int family;
process_id_t owner;
unsigned char addr[16];
unsigned int scope_id;
unsigned int port;
} ipv6;
} udp_endpoint;
/* Retrieve a list of all processes' UDP endpoints. */
@REQ(get_udp_endpoints)
@REPLY
unsigned int count;
VARARG(endpoints,udp_endpoints);
@END
/* Create a mailslot */
@REQ(create_mailslot)
unsigned int access; /* wanted access rights */

View file

@ -350,6 +350,7 @@ DECL_HANDLER(set_security_object);
DECL_HANDLER(get_security_object);
DECL_HANDLER(get_system_handles);
DECL_HANDLER(get_tcp_connections);
DECL_HANDLER(get_udp_endpoints);
DECL_HANDLER(create_mailslot);
DECL_HANDLER(set_mailslot_info);
DECL_HANDLER(create_directory);
@ -643,6 +644,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_get_security_object,
(req_handler)req_get_system_handles,
(req_handler)req_get_tcp_connections,
(req_handler)req_get_udp_endpoints,
(req_handler)req_create_mailslot,
(req_handler)req_set_mailslot_info,
(req_handler)req_create_directory,
@ -746,6 +748,7 @@ C_ASSERT( sizeof(struct thread_info) == 40 );
C_ASSERT( sizeof(tcp_connection) == 60 );
C_ASSERT( sizeof(thread_id_t) == 4 );
C_ASSERT( sizeof(timeout_t) == 8 );
C_ASSERT( sizeof(udp_endpoint) == 32 );
C_ASSERT( sizeof(unsigned char) == 1 );
C_ASSERT( sizeof(unsigned int) == 4 );
C_ASSERT( sizeof(unsigned short) == 2 );
@ -2075,6 +2078,9 @@ C_ASSERT( FIELD_OFFSET(struct get_tcp_connections_request, state_filter) == 12 )
C_ASSERT( sizeof(struct get_tcp_connections_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_tcp_connections_reply, count) == 8 );
C_ASSERT( sizeof(struct get_tcp_connections_reply) == 16 );
C_ASSERT( sizeof(struct get_udp_endpoints_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_udp_endpoints_reply, count) == 8 );
C_ASSERT( sizeof(struct get_udp_endpoints_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct create_mailslot_request, access) == 12 );
C_ASSERT( FIELD_OFFSET(struct create_mailslot_request, options) == 16 );
C_ASSERT( FIELD_OFFSET(struct create_mailslot_request, read_timeout) == 24 );

View file

@ -4269,3 +4269,70 @@ DECL_HANDLER(get_tcp_connections)
enum_handles_of_type( &sock_ops, enum_tcp_connections, &info );
}
}
struct enum_udp_endpoint_info
{
unsigned int count;
udp_endpoint *endpt;
};
static int enum_udp_endpoints( struct process *process, struct object *obj, void *user )
{
struct sock *sock = (struct sock *)obj;
struct enum_udp_endpoint_info *info = user;
udp_endpoint *endpt;
assert( obj->ops == &sock_ops );
if (sock->type != WS_SOCK_DGRAM || !(sock->family == WS_AF_INET || sock->family == WS_AF_INET6))
return 0;
if (!info->endpt)
{
info->count++;
return 0;
}
assert( info->count );
endpt = info->endpt++;
memset( endpt, 0, sizeof(*endpt) );
endpt->common.family = sock->family;
endpt->common.owner = process->id;
if (sock->family == WS_AF_INET)
{
endpt->ipv4.addr = sock->addr.in.sin_addr.WS_s_addr;
endpt->ipv4.port = sock->addr.in.sin_port;
}
else
{
memcpy( &endpt->ipv6.addr, &sock->addr.in6.sin6_addr, 16 );
endpt->ipv6.scope_id = sock->addr.in6.sin6_scope_id;
endpt->ipv6.port = sock->addr.in6.sin6_port;
}
info->count--;
return 0;
}
DECL_HANDLER(get_udp_endpoints)
{
struct enum_udp_endpoint_info info;
udp_endpoint *endpt;
data_size_t max_endpts = get_reply_max_size() / sizeof(*endpt);
info.endpt = NULL;
info.count = 0;
enum_handles_of_type( &sock_ops, enum_udp_endpoints, &info );
reply->count = info.count;
if (max_endpts < info.count)
set_error( STATUS_BUFFER_TOO_SMALL );
else if ((endpt = set_reply_data_size( info.count * sizeof(*endpt) )))
{
info.endpt = endpt;
enum_handles_of_type( &sock_ops, enum_udp_endpoints, &info );
}
}

View file

@ -1447,6 +1447,37 @@ static void dump_varargs_tcp_connections( const char *prefix, data_size_t size )
fputc( '}', stderr );
}
static void dump_varargs_udp_endpoints( const char *prefix, data_size_t size )
{
const udp_endpoint *endpt;
fprintf( stderr, "%s{", prefix );
while (size >= sizeof(*endpt))
{
endpt = cur_data;
if (endpt->common.family == WS_AF_INET)
{
char addr_str[INET_ADDRSTRLEN] = { 0 };
inet_ntop( AF_INET, (struct in_addr *)&endpt->ipv4.addr, addr_str, INET_ADDRSTRLEN );
fprintf( stderr, "{family=AF_INET,owner=%04x,addr=%s:%d}",
endpt->ipv4.owner, addr_str, endpt->ipv4.port );
}
else
{
char addr_str[INET6_ADDRSTRLEN];
inet_ntop( AF_INET6, (struct in6_addr *)&endpt->ipv6.addr, addr_str, INET6_ADDRSTRLEN );
fprintf( stderr, "{family=AF_INET6,owner=%04x,addr=[%s%%%d]:%d}",
endpt->ipv6.owner, addr_str, endpt->ipv6.scope_id, endpt->ipv6.port );
}
size -= sizeof(*endpt);
remove_data( sizeof(*endpt) );
if (size) fputc( ',', stderr );
}
fputc( '}', stderr );
}
static void dump_varargs_directory_entries( const char *prefix, data_size_t size )
{
fprintf( stderr, "%s{", prefix );
@ -4152,6 +4183,16 @@ static void dump_get_tcp_connections_reply( const struct get_tcp_connections_rep
dump_varargs_tcp_connections( ", connections=", cur_size );
}
static void dump_get_udp_endpoints_request( const struct get_udp_endpoints_request *req )
{
}
static void dump_get_udp_endpoints_reply( const struct get_udp_endpoints_reply *req )
{
fprintf( stderr, " count=%08x", req->count );
dump_varargs_udp_endpoints( ", endpoints=", cur_size );
}
static void dump_create_mailslot_request( const struct create_mailslot_request *req )
{
fprintf( stderr, " access=%08x", req->access );
@ -4952,6 +4993,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_get_security_object_request,
(dump_func)dump_get_system_handles_request,
(dump_func)dump_get_tcp_connections_request,
(dump_func)dump_get_udp_endpoints_request,
(dump_func)dump_create_mailslot_request,
(dump_func)dump_set_mailslot_info_request,
(dump_func)dump_create_directory_request,
@ -5242,6 +5284,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_get_security_object_reply,
(dump_func)dump_get_system_handles_reply,
(dump_func)dump_get_tcp_connections_reply,
(dump_func)dump_get_udp_endpoints_reply,
(dump_func)dump_create_mailslot_reply,
(dump_func)dump_set_mailslot_info_reply,
(dump_func)dump_create_directory_reply,
@ -5532,6 +5575,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"get_security_object",
"get_system_handles",
"get_tcp_connections",
"get_udp_endpoints",
"create_mailslot",
"set_mailslot_info",
"create_directory",

View file

@ -65,6 +65,7 @@ my %formats =
"select_op_t" => [ 264, 8 ],
"startup_info_t" => [ 96, 4 ],
"tcp_connection" => [ 60, 4 ],
"udp_endpoint" => [ 32, 4 ],
"user_apc_t" => [ 40, 8 ],
"struct filesystem_event" => [ 12, 4 ],
"struct handle_info" => [ 20, 4 ],