1 #Region "Microsoft.VisualBasic::ac6fa9843f259d583fe5a375834efa04, Microsoft.VisualBasic.Core\Net\Tcp\IPTools\LAN.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module LANTools
35     
36     '         FunctionGetAllDevicesOnLAN, GetIPAddress, GetIpNetTable, GetMacAddress, IsMulticast
37     '         Structure MIB_IPNETROW
38     
39     
40     
41     
42     
43     
44     ' /********************************************************************************/
45
46 #End Region
47
48 Imports System.Net
49 Imports System.Net.NetworkInformation
50 Imports System.Runtime.InteropServices
51 Imports System.Runtime.InteropServices.Marshal
52
53 Namespace Net
54
55     ''' <summary>
56     ''' http://www.codeproject.com/Tips/358946/Retrieving-IP-and-MAC-addresses-for-a-LAN
57     ''' </summary>
58     ''' <code>
59     ''' ' Get my PC IP address
60     ''' Call Console.WriteLine("My IP : {0}", GetIPAddress())
61     ''' 
62     ''' ' Get My PC MAC address
63     ''' Call Console.WriteLine("My MAC: {0}", GetMacAddress())
64     ''' 
65     ''' ' Get all devices on network
66     ''' Dim all As Dictionary(Of IPAddress, PhysicalAddress) = GetAllDevicesOnLAN()
67     ''' For Each kvp As KeyValuePair(Of IPAddress, PhysicalAddress) In all
68     '''     Console.WriteLine("IP : {0}" &amp; vbLf &amp; " MAC {1}", kvp.Key, kvp.Value)
69     ''' Next
70     ''' </code>
71     Public Module LANTools
72
73         ''' <summary>
74         ''' MIB_IPNETROW structure returned by GetIpNetTable
75         ''' DO NOT MODIFY THIS STRUCTURE.
76         ''' </summary>
77         <StructLayout(LayoutKind.Sequential)>
78         Private Structure MIB_IPNETROW
79             <MarshalAs(UnmanagedType.U4)>
80             Public dwIndex As Integer
81             <MarshalAs(UnmanagedType.U4)>
82             Public dwPhysAddrLen As Integer
83             <MarshalAs(UnmanagedType.U1)>
84             Public mac0 As Byte
85             <MarshalAs(UnmanagedType.U1)>
86             Public mac1 As Byte
87             <MarshalAs(UnmanagedType.U1)>
88             Public mac2 As Byte
89             <MarshalAs(UnmanagedType.U1)>
90             Public mac3 As Byte
91             <MarshalAs(UnmanagedType.U1)>
92             Public mac4 As Byte
93             <MarshalAs(UnmanagedType.U1)>
94             Public mac5 As Byte
95             <MarshalAs(UnmanagedType.U1)>
96             Public mac6 As Byte
97             <MarshalAs(UnmanagedType.U1)>
98             Public mac7 As Byte
99             <MarshalAs(UnmanagedType.U4)>
100             Public dwAddr As Integer
101             <MarshalAs(UnmanagedType.U4)>
102             Public dwType As Integer
103         End Structure
104
105         ''' <summary>
106         ''' GetIpNetTable external method
107         ''' </summary>
108         ''' <param name="pIpNetTable"></param>
109         ''' <param name="pdwSize"></param>
110         ''' <param name="bOrder"></param>
111         ''' <returns></returns>
112         <DllImport("IpHlpApi.dll")>
113         Private Function GetIpNetTable(pIpNetTable As IntPtr, <MarshalAs(UnmanagedType.U4)> ByRef pdwSize As Integer, bOrder As BooleanAs <MarshalAs(UnmanagedType.U4)> Integer
114         End Function
115
116         ''' <summary>
117         ''' Error codes GetIpNetTable returns that we recognise
118         ''' </summary>
119         Const ERROR_INSUFFICIENT_BUFFER As Integer = 122
120
121         ''' <summary>
122         ''' Get the IP and MAC addresses of all known devices on the LAN
123         ''' </summary>
124         ''' <remarks>
125         ''' 1) This table is not updated often - it can take some human-scale time 
126         '''    to notice that a device has dropped off the network, or a new device
127         '''    has connected.
128         ''' 2) This discards non-local devices if they are found - these are multicast
129         '''    and can be discarded by IP address range.
130         ''' </remarks>
131         ''' <returns></returns>
132         Public Function GetAllDevicesOnLAN() As Dictionary(Of IPAddress, PhysicalAddress)
133             Dim all As New Dictionary(Of IPAddress, PhysicalAddress)()
134             ' Add this PC to the list...
135             all.Add(GetIPAddress(), GetMacAddress())
136             Dim spaceForNetTable As Integer = 0
137             Get the space needed
138             ' We do that by requesting the table, but not giving any space at all.
139             ' The return value will tell us how much we actually need.
140             GetIpNetTable(IntPtr.Zero, spaceForNetTable, False)
141             ' Allocate the space
142             ' We use a try-finally block to ensure release.
143             Dim rawTable As IntPtr = IntPtr.Zero
144             Try
145                 rawTable = AllocCoTaskMem(spaceForNetTable)
146                 Get the actual data
147                 Dim errorCode As Integer = GetIpNetTable(rawTable, spaceForNetTable, False)
148                 If errorCode <> 0 Then
149                     ' Failed for some reason - can do no more here.
150                     Throw New Exception(String.Format("Unable to retrieve network table. Error code {0}", errorCode))
151                 End If
152                 Get the rows count
153                 Dim rowsCount As Integer = ReadInt32(rawTable)
154                 Dim currentBuffer As New IntPtr(rawTable.ToInt64() + SizeOf(GetType(Int32)))
155                 ' Convert the raw table to individual entries
156                 Dim rows As MIB_IPNETROW() = New MIB_IPNETROW(rowsCount - 1) {}
157                 For index As Integer = 0 To rowsCount - 1
158                     rows(index) = CType(PtrToStructure(New IntPtr(currentBuffer.ToInt64() + (index * SizeOf(GetType(MIB_IPNETROW)))), GetType(MIB_IPNETROW)), MIB_IPNETROW)
159                 Next
160                 ' Define the dummy entries list (we can discard these)
161                 Dim virtualMAC As New PhysicalAddress(New Byte() {0, 0, 0, 0, 0, 0})
162                 Dim broadcastMAC As New PhysicalAddress(New Byte() {255, 255, 255, 255, 255, 255})
163                 For Each row As MIB_IPNETROW In rows
164                     Dim ip As New IPAddress(BitConverter.GetBytes(row.dwAddr))
165                     Dim rawMAC As Byte() = New Byte() {row.mac0, row.mac1, row.mac2, row.mac3, row.mac4, row.mac5}
166                     Dim pa As New PhysicalAddress(rawMAC)
167                     If Not pa.Equals(virtualMAC) AndAlso Not pa.Equals(broadcastMAC) AndAlso Not IsMulticast(ip) Then
168                         'Console.WriteLine("IP: {0}\t\tMAC: {1}", ip.ToString(), pa.ToString());
169                         If Not all.ContainsKey(ip) Then
170                             all.Add(ip, pa)
171                         End If
172                     End If
173                 Next
174             Finally
175                 ' Release the memory.
176                 FreeCoTaskMem(rawTable)
177             End Try
178             Return all
179         End Function
180
181         ''' <summary>
182         ''' Gets the IP address of the current PC
183         ''' </summary>
184         ''' <returns></returns>
185         Public Function GetIPAddress() As IPAddress
186             Dim strHostName As String = Dns.GetHostName()
187             Dim ipEntry As IPHostEntry = Dns.GetHostEntry(strHostName)
188             Dim addr As IPAddress() = ipEntry.AddressList
189             For Each ip As IPAddress In addr
190                 If Not ip.IsIPv6LinkLocal Then
191                     Return (ip)
192                 End If
193             Next
194             Return If(addr.Length > 0, addr(0), Nothing)
195         End Function
196
197         ''' <summary>
198         ''' Gets the MAC address of the current PC.
199         ''' </summary>
200         ''' <returns></returns>
201         Public Function GetMacAddress() As PhysicalAddress
202             For Each nic As NetworkInterface In NetworkInterface.GetAllNetworkInterfaces()
203                 Only consider Ethernet network interfaces
204                 If nic.NetworkInterfaceType = NetworkInterfaceType.Ethernet AndAlso nic.OperationalStatus = OperationalStatus.Up Then
205                     Return nic.GetPhysicalAddress()
206                 End If
207             Next
208             Return Nothing
209         End Function
210
211         ''' <summary>
212         ''' Returns true if the specified IP address is a multicast address
213         ''' </summary>
214         ''' <param name="ip"></param>
215         ''' <returns></returns>
216         Public Function IsMulticast(ip As IPAddress) As Boolean
217             Dim result As Boolean = True
218             If Not ip.IsIPv6Multicast Then
219                 Dim highIP As Byte = ip.GetAddressBytes()(0)
220                 If highIP < 224 OrElse highIP > 239 Then
221                     result = False
222                 End If
223             End If
224             Return result
225         End Function
226     End Module
227 End Namespace