ws2_32: Return a valid value for WSAIoctl SIO_IDEAL_SEND_BACKLOG_QUERY.
[wine.git] / dlls / nsiproxy.sys / udp.c
blob2248d74234be4d2c559da8ea89335846764de99f
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
26 #include <stdarg.h>
27 #include <stddef.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
31 #ifdef HAVE_SYS_PARAM_H
32 #include <sys/param.h>
33 #endif
35 #ifdef HAVE_SYS_SYSCTL_H
36 #include <sys/sysctl.h>
37 #endif
39 #ifdef HAVE_SYS_SOCKETVAR_H
40 #include <sys/socketvar.h>
41 #endif
43 #ifdef HAVE_NETINET_IN_H
44 #include <netinet/in.h>
45 #endif
47 #ifdef HAVE_NETINET_IP_H
48 #include <netinet/ip.h>
49 #endif
51 #ifdef HAVE_NETINET_IP_VAR_H
52 #include <netinet/ip_var.h>
53 #endif
55 #ifdef HAVE_NETINET_IN_PCB_H
56 #include <netinet/in_pcb.h>
57 #endif
59 #ifdef HAVE_NETINET_UDP_H
60 #include <netinet/udp.h>
61 #endif
63 #ifdef HAVE_NETINET_UDP_VAR_H
64 #include <netinet/udp_var.h>
65 #endif
67 #include "ntstatus.h"
68 #define WIN32_NO_STATUS
69 #include "windef.h"
70 #include "winbase.h"
71 #include "winternl.h"
72 #define USE_WS_PREFIX
73 #include "winsock2.h"
74 #include "ifdef.h"
75 #include "netiodef.h"
76 #include "ws2ipdef.h"
77 #include "udpmib.h"
78 #include "wine/nsi.h"
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 );
111 #ifdef __linux__
112 if (*family == WS_AF_INET)
114 NTSTATUS status = STATUS_NOT_SUPPORTED;
115 static const char hdr[] = "Udp:";
116 char buf[512], *ptr;
117 FILE *fp;
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;
129 ptr += sizeof(hdr);
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;
136 break;
139 fclose( fp );
140 return status;
142 else
144 unsigned int in_dgrams = 0, out_dgrams = 0;
145 struct
147 const char *name;
148 UINT *elem;
149 } udp_stat_list[] =
151 { "Udp6InDatagrams", &in_dgrams },
152 { "Udp6NoPorts", &dyn.no_ports },
153 { "Udp6InErrors", &dyn.in_errs },
154 { "Udp6OutDatagrams", &out_dgrams },
156 char buf[512], *ptr, *value;
157 UINT res, i;
158 FILE *fp;
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 */
167 ptr = value - 1;
168 *(ptr + 1) = '\0';
170 /* and strip leading spaces from value */
171 value += 1;
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;
182 fclose( fp );
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;
200 #endif
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 )
209 UINT num = 0;
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 );
221 #ifdef __linux__
223 FILE *fp;
224 char buf[512], *ptr;
225 int inode;
226 UINT addr;
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)
240 continue;
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 */
251 if (num < *count)
253 if (key_out) *key_out++ = key;
254 if (stat_out) *stat_out++ = stat;
256 num++;
258 fclose( fp );
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)
276 continue;
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,
280 addr_scopes_size );
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 */
287 if (num < *count)
289 if (key_out) *key_out++ = key;
290 if (stat_out) *stat_out++ = stat;
292 num++;
294 fclose( fp );
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 };
300 size_t len = 0;
301 char *buf = NULL;
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;
308 goto err;
311 buf = malloc( len );
312 if (!buf)
314 status = STATUS_NO_MEMORY;
315 goto err;
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;
322 goto err;
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;
332 xig = orig_xig;
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;
341 #else
342 struct inpcb *in = &((struct xinpcb *)xig)->xi_inp;
343 struct xsocket *sock = &((struct xinpcb *)xig)->xi_socket;
344 #endif
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;
366 else
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,
372 addr_scopes_size );
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 */
380 if (num < *count)
382 if (key_out) *key_out++ = key;
383 if (stat_out) *stat_out++ = stat;
385 num++;
387 err:
388 free( buf );
390 #else
391 FIXME( "not implemented\n" );
392 return STATUS_NOT_IMPLEMENTED;
393 #endif
395 if (!want_data || num <= *count) *count = num;
396 else status = STATUS_BUFFER_OVERFLOW;
398 free( pid_map );
399 free( addr_scopes );
400 return status;
403 static struct module_table udp_tables[] =
406 NSI_UDP_STATS_TABLE,
408 sizeof(USHORT), 0,
409 sizeof(struct nsi_udp_stats_dynamic), 0
411 NULL,
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,
430 udp_tables