2 * nsiproxy.sys udp module
4 * Copyright 2003, 2006, 2011 Juan Lang
5 * Copyright 2021 Huw Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include <sys/types.h>
29 #include <sys/socket.h>
31 #ifdef HAVE_SYS_PARAM_H
32 #include <sys/param.h>
35 #ifdef HAVE_SYS_SYSCTL_H
36 #include <sys/sysctl.h>
39 #ifdef HAVE_SYS_SOCKETVAR_H
40 #include <sys/socketvar.h>
43 #ifdef HAVE_NETINET_IN_H
44 #include <netinet/in.h>
47 #ifdef HAVE_NETINET_IP_H
48 #include <netinet/ip.h>
51 #ifdef HAVE_NETINET_IP_VAR_H
52 #include <netinet/ip_var.h>
55 #ifdef HAVE_NETINET_IN_PCB_H
56 #include <netinet/in_pcb.h>
59 #ifdef HAVE_NETINET_UDP_H
60 #include <netinet/udp.h>
63 #ifdef HAVE_NETINET_UDP_VAR_H
64 #include <netinet/udp_var.h>
68 #define WIN32_NO_STATUS
79 #include "wine/debug.h"
80 #include "wine/server.h"
82 #include "unix_private.h"
84 WINE_DEFAULT_DEBUG_CHANNEL(nsi
);
86 static UINT
udp_num_addrs( USHORT family
)
88 UINT endpoint_count
= 0;
90 nsi_enumerate_all( 1, 0, &NPI_MS_UDP_MODULEID
, NSI_UDP_ENDPOINT_TABLE
,
91 NULL
, 0, NULL
, 0, NULL
, 0, NULL
, 0, &endpoint_count
);
92 /* FIXME: actually retrieve the keys and only count endpoints which match family */
93 return endpoint_count
;
96 static NTSTATUS
udp_stats_get_all_parameters( const void *key
, UINT key_size
, void *rw_data
, UINT rw_size
,
97 void *dynamic_data
, UINT dynamic_size
, void *static_data
, UINT static_size
)
99 struct nsi_udp_stats_dynamic dyn
;
100 const USHORT
*family
= key
;
102 TRACE( "%p %d %p %d %p %d %p %d\n", key
, key_size
, rw_data
, rw_size
, dynamic_data
, dynamic_size
,
103 static_data
, static_size
);
105 if (*family
!= WS_AF_INET
&& *family
!= WS_AF_INET6
) return STATUS_NOT_SUPPORTED
;
107 memset( &dyn
, 0, sizeof(dyn
) );
109 dyn
.num_addrs
= udp_num_addrs( *family
);
112 if (*family
== WS_AF_INET
)
114 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
115 static const char hdr
[] = "Udp:";
119 if (!(fp
= fopen( "/proc/net/snmp", "r" ))) return STATUS_NOT_SUPPORTED
;
121 while ((ptr
= fgets( buf
, sizeof(buf
), fp
)))
123 if (ascii_strncasecmp( buf
, hdr
, sizeof(hdr
) - 1 )) continue;
124 /* last line was a header, get another */
125 if (!(ptr
= fgets( buf
, sizeof(buf
), fp
))) break;
126 if (!ascii_strncasecmp( buf
, hdr
, sizeof(hdr
) - 1 ))
128 unsigned int in_dgrams
, out_dgrams
;
130 sscanf( ptr
, "%u %u %u %u %u",
131 &in_dgrams
, &dyn
.no_ports
, &dyn
.in_errs
, &out_dgrams
, &dyn
.num_addrs
);
132 dyn
.in_dgrams
= in_dgrams
;
133 dyn
.out_dgrams
= out_dgrams
;
134 if (dynamic_data
) *(struct nsi_udp_stats_dynamic
*)dynamic_data
= dyn
;
135 status
= STATUS_SUCCESS
;
144 unsigned int in_dgrams
= 0, out_dgrams
= 0;
151 { "Udp6InDatagrams", &in_dgrams
},
152 { "Udp6NoPorts", &dyn
.no_ports
},
153 { "Udp6InErrors", &dyn
.in_errs
},
154 { "Udp6OutDatagrams", &out_dgrams
},
156 char buf
[512], *ptr
, *value
;
160 if (!(fp
= fopen( "/proc/net/snmp6", "r" ))) return STATUS_NOT_SUPPORTED
;
162 while ((ptr
= fgets( buf
, sizeof(buf
), fp
)))
164 if (!(value
= strchr( buf
, ' ' ))) continue;
166 /* terminate the valuename */
170 /* and strip leading spaces from value */
172 while (*value
==' ') value
++;
173 if ((ptr
= strchr( value
, '\n' ))) *ptr
='\0';
175 for (i
= 0; i
< ARRAY_SIZE(udp_stat_list
); i
++)
176 if (!ascii_strcasecmp( buf
, udp_stat_list
[i
].name
) && sscanf( value
, "%d", &res
))
177 *udp_stat_list
[i
].elem
= res
;
179 dyn
.in_dgrams
= in_dgrams
;
180 dyn
.out_dgrams
= out_dgrams
;
181 if (dynamic_data
) *(struct nsi_udp_stats_dynamic
*)dynamic_data
= dyn
;
183 return STATUS_SUCCESS
;
185 #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS) && defined(HAVE_STRUCT_UDPSTAT_UDPS_IPACKETS)
187 int mib
[] = { CTL_NET
, PF_INET
, IPPROTO_UDP
, UDPCTL_STATS
};
188 struct udpstat udp_stat
;
189 size_t needed
= sizeof(udp_stat
);
191 if (sysctl( mib
, ARRAY_SIZE(mib
), &udp_stat
, &needed
, NULL
, 0 ) == -1) return STATUS_NOT_SUPPORTED
;
193 dyn
.in_dgrams
= udp_stat
.udps_ipackets
;
194 dyn
.out_dgrams
= udp_stat
.udps_opackets
;
195 dyn
.no_ports
= udp_stat
.udps_noport
;
196 dyn
.in_errs
= udp_stat
.udps_hdrops
+ udp_stat
.udps_badsum
+ udp_stat
.udps_fullsock
+ udp_stat
.udps_badlen
;
197 if (dynamic_data
) *(struct nsi_udp_stats_dynamic
*)dynamic_data
= dyn
;
198 return STATUS_SUCCESS
;
201 FIXME( "Not implemented\n" );
202 return STATUS_NOT_SUPPORTED
;
205 static NTSTATUS
udp_endpoint_enumerate_all( void *key_data
, UINT key_size
, void *rw_data
, UINT rw_size
,
206 void *dynamic_data
, UINT dynamic_size
,
207 void *static_data
, UINT static_size
, UINT_PTR
*count
)
210 NTSTATUS status
= STATUS_SUCCESS
;
211 BOOL want_data
= key_size
|| rw_size
|| dynamic_size
|| static_size
;
212 struct nsi_udp_endpoint_key key
, *key_out
= key_data
;
213 struct nsi_udp_endpoint_static stat
, *stat_out
= static_data
;
214 struct ipv6_addr_scope
*addr_scopes
= NULL
;
215 unsigned int addr_scopes_size
= 0, pid_map_size
= 0;
216 struct pid_map
*pid_map
= NULL
;
218 TRACE( "%p %d %p %d %p %d %p %d %p\n", key_data
, key_size
, rw_data
, rw_size
,
219 dynamic_data
, dynamic_size
, static_data
, static_size
, count
);
228 if (!(fp
= fopen( "/proc/net/udp", "r" ))) return ERROR_NOT_SUPPORTED
;
230 memset( &key
, 0, sizeof(key
) );
231 memset( &stat
, 0, sizeof(stat
) );
232 pid_map
= get_pid_map( &pid_map_size
);
234 /* skip header line */
235 ptr
= fgets( buf
, sizeof(buf
), fp
);
236 while ((ptr
= fgets( buf
, sizeof(buf
), fp
)))
238 if (sscanf( ptr
, "%*u: %x:%hx %*s %*s %*s %*s %*s %*s %*s %d",
239 &addr
, &key
.local
.Ipv4
.sin_port
, &inode
) != 3)
242 key
.local
.Ipv4
.sin_family
= WS_AF_INET
;
243 key
.local
.Ipv4
.sin_addr
.WS_s_addr
= addr
;
244 key
.local
.Ipv4
.sin_port
= htons( key
.local
.Ipv4
.sin_port
);
246 stat
.pid
= find_owning_pid( pid_map
, pid_map_size
, inode
);
247 stat
.create_time
= 0; /* FIXME */
248 stat
.flags
= 0; /* FIXME */
249 stat
.mod_info
= 0; /* FIXME */
253 if (key_out
) *key_out
++ = key
;
254 if (stat_out
) *stat_out
++ = stat
;
260 if ((fp
= fopen( "/proc/net/udp6", "r" )))
262 memset( &key
, 0, sizeof(key
) );
263 memset( &stat
, 0, sizeof(stat
) );
265 addr_scopes
= get_ipv6_addr_scope_table( &addr_scopes_size
);
267 /* skip header line */
268 ptr
= fgets( buf
, sizeof(buf
), fp
);
269 while ((ptr
= fgets( buf
, sizeof(buf
), fp
)))
271 UINT
*local_addr
= (UINT
*)&key
.local
.Ipv6
.sin6_addr
;
273 if (sscanf( ptr
, "%*u: %8x%8x%8x%8x:%hx %*s %*s %*s %*s %*s %*s %*s %d",
274 local_addr
, local_addr
+ 1, local_addr
+ 2, local_addr
+ 3,
275 &key
.local
.Ipv6
.sin6_port
, &inode
) != 6)
277 key
.local
.Ipv6
.sin6_family
= WS_AF_INET6
;
278 key
.local
.Ipv6
.sin6_port
= htons( key
.local
.Ipv6
.sin6_port
);
279 key
.local
.Ipv6
.sin6_scope_id
= find_ipv6_addr_scope( &key
.local
.Ipv6
.sin6_addr
, addr_scopes
,
282 stat
.pid
= find_owning_pid( pid_map
, pid_map_size
, inode
);
283 stat
.create_time
= 0; /* FIXME */
284 stat
.flags
= 0; /* FIXME */
285 stat
.mod_info
= 0; /* FIXME */
289 if (key_out
) *key_out
++ = key
;
290 if (stat_out
) *stat_out
++ = stat
;
297 #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_PCBLIST) && defined(HAVE_STRUCT_XINPGEN)
299 int mib
[] = { CTL_NET
, PF_INET
, IPPROTO_UDP
, UDPCTL_PCBLIST
};
302 struct xinpgen
*xig
, *orig_xig
;
304 if (sysctl( mib
, ARRAY_SIZE(mib
), NULL
, &len
, NULL
, 0 ) < 0)
306 ERR( "Failure to read net.inet.udp.pcblist via sysctlbyname!\n" );
307 status
= STATUS_NOT_SUPPORTED
;
314 status
= STATUS_NO_MEMORY
;
318 if (sysctl( mib
, ARRAY_SIZE(mib
), buf
, &len
, NULL
, 0 ) < 0)
320 ERR( "Failure to read net.inet.udp.pcblist via sysctlbyname!\n" );
321 status
= STATUS_NOT_SUPPORTED
;
325 /* Might be nothing here; first entry is just a header it seems */
326 if (len
<= sizeof(struct xinpgen
)) goto err
;
328 addr_scopes
= get_ipv6_addr_scope_table( &addr_scopes_size
);
329 pid_map
= get_pid_map( &pid_map_size
);
331 orig_xig
= (struct xinpgen
*)buf
;
334 for (xig
= (struct xinpgen
*)((char *)xig
+ xig
->xig_len
);
335 xig
->xig_len
> sizeof (struct xinpgen
);
336 xig
= (struct xinpgen
*)((char *)xig
+ xig
->xig_len
))
338 #if __FreeBSD_version >= 1200026
339 struct xinpcb
*in
= (struct xinpcb
*)xig
;
340 struct xsocket
*sock
= &in
->xi_socket
;
342 struct inpcb
*in
= &((struct xinpcb
*)xig
)->xi_inp
;
343 struct xsocket
*sock
= &((struct xinpcb
*)xig
)->xi_socket
;
345 static const struct in6_addr zero
;
347 /* Ignore sockets for other protocols */
348 if (sock
->xso_protocol
!= IPPROTO_UDP
) continue;
350 /* Ignore PCBs that were freed while generating the data */
351 if (in
->inp_gencnt
> orig_xig
->xig_gen
) continue;
353 /* we're only interested in IPv4 and IPv6 addresses */
354 if (!(in
->inp_vflag
& (INP_IPV4
| INP_IPV6
))) continue;
356 /* If all 0's, skip it */
357 if (in
->inp_vflag
& INP_IPV4
&& !in
->inp_laddr
.s_addr
) continue;
358 if (in
->inp_vflag
& INP_IPV6
&& !memcmp( &in
->in6p_laddr
, &zero
, sizeof(zero
) ) && !in
->inp_lport
) continue;
360 if (in
->inp_vflag
& INP_IPV4
)
362 key
.local
.Ipv4
.sin_family
= WS_AF_INET
;
363 key
.local
.Ipv4
.sin_addr
.WS_s_addr
= in
->inp_laddr
.s_addr
;
364 key
.local
.Ipv4
.sin_port
= in
->inp_lport
;
368 key
.local
.Ipv6
.sin6_family
= WS_AF_INET6
;
369 memcpy( &key
.local
.Ipv6
.sin6_addr
, &in
->in6p_laddr
, sizeof(in
->in6p_laddr
) );
370 key
.local
.Ipv6
.sin6_port
= in
->inp_lport
;
371 key
.local
.Ipv6
.sin6_scope_id
= find_ipv6_addr_scope( &key
.local
.Ipv6
.sin6_addr
, addr_scopes
,
375 stat
.pid
= find_owning_pid( pid_map
, pid_map_size
, (UINT_PTR
)sock
->so_pcb
);
376 stat
.create_time
= 0; /* FIXME */
377 stat
.flags
= !(in
->inp_flags
& INP_ANONPORT
);
378 stat
.mod_info
= 0; /* FIXME */
382 if (key_out
) *key_out
++ = key
;
383 if (stat_out
) *stat_out
++ = stat
;
391 FIXME( "not implemented\n" );
392 return STATUS_NOT_IMPLEMENTED
;
395 if (!want_data
|| num
<= *count
) *count
= num
;
396 else status
= STATUS_BUFFER_OVERFLOW
;
403 static struct module_table udp_tables
[] =
409 sizeof(struct nsi_udp_stats_dynamic
), 0
412 udp_stats_get_all_parameters
,
415 NSI_UDP_ENDPOINT_TABLE
,
417 sizeof(struct nsi_udp_endpoint_key
), 0,
418 0, sizeof(struct nsi_udp_endpoint_static
)
420 udp_endpoint_enumerate_all
,
427 const struct module udp_module
=
429 &NPI_MS_UDP_MODULEID
,