Merge pull request #3028 from lateralusX/jlorenss/threadpool_warning
[mono.git] / mcs / class / referencesource / System / net / System / Net / NetworkInformation / SystemIPGlobalProperties.cs
1
2
3 namespace System.Net.NetworkInformation {
4
5     using System.Net;
6     using System.Net.Sockets;
7     using System;
8     using System.Runtime.InteropServices;
9     using System.Threading;
10     using System.Collections.Generic;
11
12 #if !MONO
13     internal class SystemIPGlobalProperties:IPGlobalProperties {
14         private FixedInfo fixedInfo;
15         private bool fixedInfoInitialized = false;
16     
17         //changing these require a reboot, so we'll cache them instead.
18         private static volatile string hostName = null;
19         private static volatile string domainName = null;
20
21         static object syncObject = new object();
22
23         internal SystemIPGlobalProperties() {
24         }
25
26         internal static FixedInfo GetFixedInfo(){
27             uint    size = 0;
28             SafeLocalFree buffer = null; 
29             FixedInfo fixedInfo = new FixedInfo();
30             
31             //first we need to get the size of the buffer
32             uint result = UnsafeNetInfoNativeMethods.GetNetworkParams(SafeLocalFree.Zero,ref size);
33             
34             while (result == IpHelperErrors.ErrorBufferOverflow) {
35                 try {
36                     //now we allocate the buffer and read the network parameters.
37                     buffer = SafeLocalFree.LocalAlloc((int)size);
38                     result = UnsafeNetInfoNativeMethods.GetNetworkParams(buffer,ref size);
39                     if ( result == IpHelperErrors.Success ) {
40                         fixedInfo = new FixedInfo( (FIXED_INFO)Marshal.PtrToStructure(buffer.DangerousGetHandle(),typeof(FIXED_INFO)));
41                     }
42                 }
43                 finally {
44                     if(buffer != null){
45                         buffer.Close();
46                     }
47                 }
48             }
49             
50             //if the result include there being no information, we'll still throw
51             if (result != IpHelperErrors.Success) {
52                 throw new NetworkInformationException((int)result);
53             }
54             return fixedInfo; 
55         }
56
57         
58         internal FixedInfo FixedInfo {
59             get {
60                 if (!fixedInfoInitialized) {
61                     lock(this){
62                         if (!fixedInfoInitialized) {
63                             fixedInfo = GetFixedInfo();
64                             fixedInfoInitialized = true;
65                         }
66                     }
67                 }
68                 return fixedInfo;
69             }
70         }
71
72         /// <summary>Specifies the host name for the local computer.</summary>
73         public override string HostName{
74             get {
75                 if(hostName == null){
76                     lock(syncObject){
77                         if(hostName == null){
78                             hostName = FixedInfo.HostName;
79                             domainName = FixedInfo.DomainName;
80                         }
81                     }
82                 }
83                 return hostName;
84             }
85         }
86         /// <summary>Specifies the domain in which the local computer is registered.</summary>
87         public override string DomainName{
88             get {
89                 if(domainName == null){
90                     lock(syncObject){
91                         if(domainName == null){
92                             hostName = FixedInfo.HostName;
93                             domainName = FixedInfo.DomainName;
94                         }
95                     }
96                 }
97                 return domainName;
98             }
99         }
100         /// <summary>
101         /// The type of node.
102         /// </summary>
103         /// <remarks>
104         /// The exact mechanism by which NetBIOS names are resolved to IP addresses
105         /// depends on the node's configured NetBIOS Node Type. Broadcast - uses broadcast
106         /// NetBIOS Name Queries for name registration and resolution.
107         /// PeerToPeer - uses a NetBIOS name server (NBNS), such as Windows Internet
108         /// Name Service (WINS), to resolve NetBIOS names.
109         /// Mixed - uses Broadcast then PeerToPeer.
110         /// Hybrid - uses PeerToPeer then Broadcast.
111         /// </remarks>
112         public override NetBiosNodeType NodeType{get {
113             return (NetBiosNodeType)FixedInfo.NodeType;}
114         }
115         /// <summary>Specifies the DHCP scope name.</summary>
116         public override string DhcpScopeName{get {
117             return FixedInfo.ScopeId;}
118         }
119         /// <summary>Specifies whether the local computer is acting as an WINS proxy.</summary>
120         public override bool IsWinsProxy{get {
121             return (FixedInfo.EnableProxy);}
122         }
123
124           
125         public override TcpConnectionInformation[] GetActiveTcpConnections(){
126             List<TcpConnectionInformation> list = new List<TcpConnectionInformation>();
127             List<SystemTcpConnectionInformation> connections = GetAllTcpConnections();
128             foreach(TcpConnectionInformation connection in connections){
129                 if(connection.State != TcpState.Listen){
130                     list.Add(connection);
131                 }
132             }
133             return list.ToArray();
134         }
135
136
137         public override IPEndPoint[] GetActiveTcpListeners (){
138             List<IPEndPoint> list = new List<IPEndPoint>();
139             List<SystemTcpConnectionInformation> connections = GetAllTcpConnections();
140             foreach(TcpConnectionInformation connection in connections){
141                 if(connection.State == TcpState.Listen){
142                     list.Add(connection.LocalEndPoint);
143                 }
144             }
145             return list.ToArray();
146         }
147
148
149
150         /// <summary>
151         /// Gets the active tcp connections. Uses the native GetTcpTable api.</summary>
152         private List<SystemTcpConnectionInformation> GetAllTcpConnections() {
153             uint size = 0;
154             uint result = 0;
155             SafeLocalFree buffer = null;
156             List<SystemTcpConnectionInformation> tcpConnections = new List<SystemTcpConnectionInformation>();
157
158             // Check if it supports IPv4 for IPv6 only modes.
159             if (Socket.OSSupportsIPv4) {
160
161                 // Get the size of buffer needed
162                 result = UnsafeNetInfoNativeMethods.GetTcpTable(SafeLocalFree.Zero, ref size, true);
163
164                 while (result == IpHelperErrors.ErrorInsufficientBuffer) {
165                     try {
166                         //allocate the buffer and get the tcptable
167                         buffer = SafeLocalFree.LocalAlloc((int)size);
168                         result = UnsafeNetInfoNativeMethods.GetTcpTable(buffer, ref size, true);
169
170                         if (result == IpHelperErrors.Success) {
171                             //the table info just gives us the number of rows.
172                             IntPtr newPtr = buffer.DangerousGetHandle();
173                             MibTcpTable tcpTableInfo = (MibTcpTable)Marshal.PtrToStructure(newPtr, typeof(MibTcpTable));
174
175                             if (tcpTableInfo.numberOfEntries > 0) {
176                                 //we need to skip over the tableinfo to get the inline rows
177                                 newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(tcpTableInfo.numberOfEntries));
178
179                                 for (int i = 0; i < tcpTableInfo.numberOfEntries; i++) {
180                                     MibTcpRow tcpRow = (MibTcpRow)Marshal.PtrToStructure(newPtr, typeof(MibTcpRow));
181                                     tcpConnections.Add(new SystemTcpConnectionInformation(tcpRow));
182
183                                     //we increment the pointer to the next row
184                                     newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(tcpRow));
185                                 }
186                             }
187                         }
188                     }
189                     finally {
190                         if (buffer != null)
191                             buffer.Close();
192                     }
193                 }
194
195                 // if we don't have any ipv4 interfaces detected, just continue
196                 if (result != IpHelperErrors.Success && result != IpHelperErrors.ErrorNoData) {
197                     throw new NetworkInformationException((int)result);
198                 }
199             }
200
201             if (Socket.OSSupportsIPv6) {
202
203                 // IPv6 tcp connections
204                 // Get the size of buffer needed
205                 size = 0;
206                 result = UnsafeNetInfoNativeMethods.GetExtendedTcpTable(SafeLocalFree.Zero, ref size, true,
207                                                                         (uint)AddressFamily.InterNetworkV6,
208                                                                         TcpTableClass.TcpTableOwnerPidAll, 0);
209
210                 while (result == IpHelperErrors.ErrorInsufficientBuffer) {
211                     try {
212                         // Allocate the buffer and get the tcptable
213                         buffer = SafeLocalFree.LocalAlloc((int)size);
214                         result = UnsafeNetInfoNativeMethods.GetExtendedTcpTable(buffer, ref size, true,
215                                                                                 (uint)AddressFamily.InterNetworkV6,
216                                                                                 TcpTableClass.TcpTableOwnerPidAll, 0);
217                         if (result == IpHelperErrors.Success) {
218                             // The table info just gives us the number of rows.
219                             IntPtr newPtr = buffer.DangerousGetHandle();
220
221                             MibTcp6TableOwnerPid tcpTable6OwnerPid 
222                                 = (MibTcp6TableOwnerPid)Marshal.PtrToStructure(newPtr, typeof(MibTcp6TableOwnerPid));
223
224                             if (tcpTable6OwnerPid.numberOfEntries > 0) {
225                                 // We need to skip over the tableinfo to get the inline rows
226                                 newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(tcpTable6OwnerPid.numberOfEntries));
227
228                                 for (int i = 0; i < tcpTable6OwnerPid.numberOfEntries; i++) {
229                                     MibTcp6RowOwnerPid tcp6RowOwnerPid 
230                                         = (MibTcp6RowOwnerPid)Marshal.PtrToStructure(newPtr, 
231                                         typeof(MibTcp6RowOwnerPid));
232                                     tcpConnections.Add(new SystemTcpConnectionInformation(tcp6RowOwnerPid));
233
234                                     // We increment the pointer to the next row
235                                     newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(tcp6RowOwnerPid));
236                                 }
237                             }
238                         }
239                     }
240                     finally {
241                         if (buffer != null)
242                             buffer.Close();
243                     }
244                 }
245
246                 // If we don't have any ipv6 interfaces detected, just continue
247                 if (result != IpHelperErrors.Success && result != IpHelperErrors.ErrorNoData) {
248                     throw new NetworkInformationException((int)result);
249                 }
250             }
251
252             return tcpConnections;
253         }
254
255
256
257
258         /// <summary>Gets the active udp listeners. Uses the native GetUdpTable api.</summary>
259         public override IPEndPoint[] GetActiveUdpListeners(){
260             uint    size = 0;
261             uint result = 0;
262             SafeLocalFree buffer = null;
263             List<IPEndPoint> udpListeners = new List<IPEndPoint>();
264
265             // Check if it support IPv4 for IPv6 only modes.
266             if (Socket.OSSupportsIPv4) {
267                 // Get the size of buffer needed
268                 result = UnsafeNetInfoNativeMethods.GetUdpTable(SafeLocalFree.Zero, ref size, true);
269                 while (result == IpHelperErrors.ErrorInsufficientBuffer) {
270                     try {
271                         //allocate the buffer and get the udptable
272                         buffer = SafeLocalFree.LocalAlloc((int)size);
273                         result = UnsafeNetInfoNativeMethods.GetUdpTable(buffer, ref size, true);
274
275                         if (result == IpHelperErrors.Success) {
276                             //the table info just gives us the number of rows.
277                             IntPtr newPtr = buffer.DangerousGetHandle();
278                             MibUdpTable udpTableInfo = (MibUdpTable)Marshal.PtrToStructure(newPtr, typeof(MibUdpTable));
279
280                             if (udpTableInfo.numberOfEntries > 0) {
281                                 //we need to skip over the tableinfo to get the inline rows
282                                 newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(udpTableInfo.numberOfEntries));
283                                 for (int i = 0; i < udpTableInfo.numberOfEntries; i++) {
284                                     MibUdpRow udpRow = (MibUdpRow)Marshal.PtrToStructure(newPtr, typeof(MibUdpRow));
285                                     int localPort = udpRow.localPort1 << 8 | udpRow.localPort2;
286
287                                     udpListeners.Add(new IPEndPoint(udpRow.localAddr, (int)localPort));
288                                     
289                                     //we increment the pointer to the next row
290                                     newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(udpRow));
291                                 }
292                             }
293                         }
294                     }
295                     finally {
296                         if (buffer != null)
297                             buffer.Close();
298                     }
299                 }
300                 // if we don't have any ipv4 interfaces detected, just continue
301                 if (result != IpHelperErrors.Success && result != IpHelperErrors.ErrorNoData) {
302                     throw new NetworkInformationException((int)result);
303                 }
304             }
305
306             if (Socket.OSSupportsIPv6) {
307
308                 // Get the size of buffer needed
309                 size = 0;
310                 result = UnsafeNetInfoNativeMethods.GetExtendedUdpTable(SafeLocalFree.Zero, ref size, true,
311                                                                         (uint)AddressFamily.InterNetworkV6,
312                                                                         UdpTableClass.UdpTableOwnerPid, 0);
313                 while (result == IpHelperErrors.ErrorInsufficientBuffer) {
314                     try {
315                         // Allocate the buffer and get the udptable
316                         buffer = SafeLocalFree.LocalAlloc((int)size);
317                         result = UnsafeNetInfoNativeMethods.GetExtendedUdpTable(buffer, ref size, true,
318                                                                                 (uint)AddressFamily.InterNetworkV6,
319                                                                                 UdpTableClass.UdpTableOwnerPid, 0);
320
321                         if (result == IpHelperErrors.Success) {
322                             // The table info just gives us the number of rows.
323                             IntPtr newPtr = buffer.DangerousGetHandle();
324                             MibUdp6TableOwnerPid udp6TableOwnerPid 
325                                 = (MibUdp6TableOwnerPid)Marshal.PtrToStructure(newPtr, typeof(MibUdp6TableOwnerPid));
326
327                             if (udp6TableOwnerPid.numberOfEntries > 0) {
328                                 // We need to skip over the tableinfo to get the inline rows
329                                 newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(udp6TableOwnerPid.numberOfEntries));
330                                 for (int i = 0; i < udp6TableOwnerPid.numberOfEntries; i++) {
331                                     MibUdp6RowOwnerPid udp6RowOwnerPid  
332                                         = (MibUdp6RowOwnerPid)Marshal.PtrToStructure(newPtr, 
333                                         typeof(MibUdp6RowOwnerPid));
334                                     int localPort = udp6RowOwnerPid.localPort1 << 8 | udp6RowOwnerPid.localPort2;
335
336                                     udpListeners.Add(new IPEndPoint(new IPAddress(udp6RowOwnerPid.localAddr, 
337                                         udp6RowOwnerPid.localScopeId), localPort));
338
339                                     // We increment the pointer to the next row
340                                     newPtr = (IntPtr)((long)newPtr + Marshal.SizeOf(udp6RowOwnerPid));
341                                 }
342                             }
343                         }
344                     }
345                     finally {
346                         if (buffer != null)
347                             buffer.Close();
348                     }
349                 }
350                 // If we don't have any ipv6 interfaces detected, just continue
351                 if (result != IpHelperErrors.Success && result != IpHelperErrors.ErrorNoData) {
352                     throw new NetworkInformationException((int)result);
353                 }
354             }
355
356             return udpListeners.ToArray();
357         }
358
359         public override IPGlobalStatistics GetIPv4GlobalStatistics(){
360             return new SystemIPGlobalStatistics(AddressFamily.InterNetwork);
361         }
362         public override IPGlobalStatistics GetIPv6GlobalStatistics(){
363             return new SystemIPGlobalStatistics(AddressFamily.InterNetworkV6);
364         }
365         
366        public override TcpStatistics GetTcpIPv4Statistics(){
367             return new SystemTcpStatistics(AddressFamily.InterNetwork);
368         }
369         public override TcpStatistics GetTcpIPv6Statistics(){
370             return new SystemTcpStatistics(AddressFamily.InterNetworkV6);
371         }
372
373         public override UdpStatistics GetUdpIPv4Statistics(){
374             return new SystemUdpStatistics(AddressFamily.InterNetwork);
375         }
376         public override UdpStatistics GetUdpIPv6Statistics(){
377             return new SystemUdpStatistics(AddressFamily.InterNetworkV6);
378         }
379          
380         public override IcmpV4Statistics GetIcmpV4Statistics(){
381             return new SystemIcmpV4Statistics();
382         }
383         
384         public override IcmpV6Statistics GetIcmpV6Statistics(){
385             return new SystemIcmpV6Statistics();
386         }
387
388         public override UnicastIPAddressInformationCollection GetUnicastAddresses(){
389             // Wait for the Address Table to stabilize
390             using (ManualResetEvent stable = new ManualResetEvent(false)) {
391                 if (!TeredoHelper.UnsafeNotifyStableUnicastIpAddressTable(StableUnicastAddressTableCallback, stable)) {
392                     stable.WaitOne();
393                 }
394             }
395
396             return GetUnicastAddressTable();
397         }
398
399         public override IAsyncResult BeginGetUnicastAddresses(AsyncCallback callback, object state){
400             ContextAwareResult asyncResult = new ContextAwareResult(false, false, this, state, callback);
401             asyncResult.StartPostingAsyncOp(false);
402             if (TeredoHelper.UnsafeNotifyStableUnicastIpAddressTable(StableUnicastAddressTableCallback, asyncResult)) {
403                 asyncResult.InvokeCallback();
404             }
405             asyncResult.FinishPostingAsyncOp();
406
407             return asyncResult;
408         }
409
410         public override UnicastIPAddressInformationCollection EndGetUnicastAddresses(IAsyncResult asyncResult){
411             if (asyncResult == null) {
412                 throw new ArgumentNullException("asyncResult");
413             }
414
415             ContextAwareResult result = asyncResult as ContextAwareResult;
416             if (result == null || result.AsyncObject == null || result.AsyncObject.GetType() != typeof(SystemIPGlobalProperties)) {
417                 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult));
418             }
419
420             if (result.EndCalled) {
421                 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetStableUnicastAddresses"));
422             }
423
424             result.InternalWaitForCompletion();
425
426             result.EndCalled = true;
427             return GetUnicastAddressTable();
428         }
429
430         private static void StableUnicastAddressTableCallback(object param){
431             EventWaitHandle handle = param as EventWaitHandle;
432             if (handle != null) {
433                 handle.Set();
434             }
435             else {
436                 LazyAsyncResult asyncResult = (LazyAsyncResult)param;
437                 asyncResult.InvokeCallback();
438             }
439         }
440
441         private static UnicastIPAddressInformationCollection GetUnicastAddressTable(){
442             UnicastIPAddressInformationCollection rval = new UnicastIPAddressInformationCollection();
443             
444             NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
445             for (int i = 0; i < interfaces.Length; ++i) {
446                 UnicastIPAddressInformationCollection addresses = interfaces[i].GetIPProperties().UnicastAddresses;
447
448                 foreach (UnicastIPAddressInformation address in addresses) {
449                     if (!rval.Contains(address)) {
450                         rval.InternalAdd(address);
451                     }
452                 }
453             }
454
455             return rval;
456         }
457
458     }   //ends networkinformation class
459
460 #endif   
461
462     internal struct FixedInfo{
463         internal FIXED_INFO info;
464
465         internal FixedInfo(FIXED_INFO info){
466             this.info = info;
467         }
468         
469         internal string HostName{
470             get{
471                 return info.hostName;
472             }
473         }
474             
475         internal string DomainName{
476             get{
477                 return info.domainName;
478             }
479         }
480         
481         internal NetBiosNodeType NodeType{
482             get{
483                 return info.nodeType;
484             }
485         }
486         internal string ScopeId{
487             get{
488                 return info.scopeId;
489             }
490         }
491
492         internal bool EnableRouting{
493             get{
494                 return info.enableRouting;
495             }
496         }
497
498         internal bool EnableProxy{
499             get{
500                 return info.enableProxy;
501             }
502         }
503
504         internal bool EnableDns{
505             get{
506                 return info.enableDns;
507             }
508         }
509     }
510 }
511