View Javadoc
1   /*
2    * Copyright 2020-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.software.os.windows;
6   
7   import static com.sun.jna.platform.win32.IPHlpAPI.AF_INET;
8   import static com.sun.jna.platform.win32.IPHlpAPI.AF_INET6;
9   import static com.sun.jna.platform.win32.IPHlpAPI.TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL;
10  import static com.sun.jna.platform.win32.IPHlpAPI.UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID;
11  import static oshi.software.os.InternetProtocolStats.TcpState.CLOSED;
12  import static oshi.software.os.InternetProtocolStats.TcpState.CLOSE_WAIT;
13  import static oshi.software.os.InternetProtocolStats.TcpState.CLOSING;
14  import static oshi.software.os.InternetProtocolStats.TcpState.ESTABLISHED;
15  import static oshi.software.os.InternetProtocolStats.TcpState.FIN_WAIT_1;
16  import static oshi.software.os.InternetProtocolStats.TcpState.FIN_WAIT_2;
17  import static oshi.software.os.InternetProtocolStats.TcpState.LAST_ACK;
18  import static oshi.software.os.InternetProtocolStats.TcpState.LISTEN;
19  import static oshi.software.os.InternetProtocolStats.TcpState.SYN_RECV;
20  import static oshi.software.os.InternetProtocolStats.TcpState.SYN_SENT;
21  import static oshi.software.os.InternetProtocolStats.TcpState.TIME_WAIT;
22  import static oshi.software.os.InternetProtocolStats.TcpState.UNKNOWN;
23  
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  
28  import com.sun.jna.Memory;
29  import com.sun.jna.platform.win32.IPHlpAPI;
30  import com.sun.jna.platform.win32.IPHlpAPI.MIB_TCP6ROW_OWNER_PID;
31  import com.sun.jna.platform.win32.IPHlpAPI.MIB_TCP6TABLE_OWNER_PID;
32  import com.sun.jna.platform.win32.IPHlpAPI.MIB_TCPROW_OWNER_PID;
33  import com.sun.jna.platform.win32.IPHlpAPI.MIB_TCPTABLE_OWNER_PID;
34  import com.sun.jna.platform.win32.IPHlpAPI.MIB_UDP6ROW_OWNER_PID;
35  import com.sun.jna.platform.win32.IPHlpAPI.MIB_UDP6TABLE_OWNER_PID;
36  import com.sun.jna.platform.win32.IPHlpAPI.MIB_UDPROW_OWNER_PID;
37  import com.sun.jna.platform.win32.IPHlpAPI.MIB_UDPTABLE_OWNER_PID;
38  import com.sun.jna.platform.win32.VersionHelpers;
39  import com.sun.jna.platform.win32.WinError;
40  
41  import oshi.annotation.concurrent.ThreadSafe;
42  import oshi.jna.ByRef.CloseableIntByReference;
43  import oshi.jna.Struct.CloseableMibTcpStats;
44  import oshi.jna.Struct.CloseableMibUdpStats;
45  import oshi.software.common.AbstractInternetProtocolStats;
46  import oshi.util.ParseUtil;
47  
48  /**
49   * Internet Protocol Stats implementation
50   */
51  @ThreadSafe
52  public class WindowsInternetProtocolStats extends AbstractInternetProtocolStats {
53  
54      private static final IPHlpAPI IPHLP = IPHlpAPI.INSTANCE;
55  
56      private static final boolean IS_VISTA_OR_GREATER = VersionHelpers.IsWindowsVistaOrGreater();
57  
58      @Override
59      public TcpStats getTCPv4Stats() {
60          try (CloseableMibTcpStats stats = new CloseableMibTcpStats()) {
61              IPHLP.GetTcpStatisticsEx(stats, AF_INET);
62              return new TcpStats(stats.dwCurrEstab, stats.dwActiveOpens, stats.dwPassiveOpens, stats.dwAttemptFails,
63                      stats.dwEstabResets, stats.dwOutSegs, stats.dwInSegs, stats.dwRetransSegs, stats.dwInErrs,
64                      stats.dwOutRsts);
65          }
66      }
67  
68      @Override
69      public TcpStats getTCPv6Stats() {
70          try (CloseableMibTcpStats stats = new CloseableMibTcpStats()) {
71              IPHLP.GetTcpStatisticsEx(stats, AF_INET6);
72              return new TcpStats(stats.dwCurrEstab, stats.dwActiveOpens, stats.dwPassiveOpens, stats.dwAttemptFails,
73                      stats.dwEstabResets, stats.dwOutSegs, stats.dwInSegs, stats.dwRetransSegs, stats.dwInErrs,
74                      stats.dwOutRsts);
75          }
76      }
77  
78      @Override
79      public UdpStats getUDPv4Stats() {
80          try (CloseableMibUdpStats stats = new CloseableMibUdpStats()) {
81              IPHLP.GetUdpStatisticsEx(stats, AF_INET);
82              return new UdpStats(stats.dwOutDatagrams, stats.dwInDatagrams, stats.dwNoPorts, stats.dwInErrors);
83          }
84      }
85  
86      @Override
87      public UdpStats getUDPv6Stats() {
88          try (CloseableMibUdpStats stats = new CloseableMibUdpStats()) {
89              IPHLP.GetUdpStatisticsEx(stats, AF_INET6);
90              return new UdpStats(stats.dwOutDatagrams, stats.dwInDatagrams, stats.dwNoPorts, stats.dwInErrors);
91          }
92      }
93  
94      @Override
95      public List<IPConnection> getConnections() {
96          if (IS_VISTA_OR_GREATER) {
97              List<IPConnection> conns = new ArrayList<>();
98              conns.addAll(queryTCPv4Connections());
99              conns.addAll(queryTCPv6Connections());
100             conns.addAll(queryUDPv4Connections());
101             conns.addAll(queryUDPv6Connections());
102             return conns;
103         }
104         return Collections.emptyList();
105     }
106 
107     private static List<IPConnection> queryTCPv4Connections() {
108         List<IPConnection> conns = new ArrayList<>();
109         // Get size needed
110         try (CloseableIntByReference sizePtr = new CloseableIntByReference()) {
111             int ret = IPHLP.GetExtendedTcpTable(null, sizePtr, false, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
112             // Get buffer and populate table
113             int size = sizePtr.getValue();
114             Memory buf = new Memory(size);
115             do {
116                 ret = IPHLP.GetExtendedTcpTable(buf, sizePtr, false, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
117                 if (ret == WinError.ERROR_INSUFFICIENT_BUFFER) {
118                     size = sizePtr.getValue();
119                     buf.close();
120                     buf = new Memory(size);
121                 }
122             } while (ret == WinError.ERROR_INSUFFICIENT_BUFFER);
123             MIB_TCPTABLE_OWNER_PID tcpTable = new MIB_TCPTABLE_OWNER_PID(buf);
124             for (int i = 0; i < tcpTable.dwNumEntries; i++) {
125                 MIB_TCPROW_OWNER_PID row = tcpTable.table[i];
126                 conns.add(new IPConnection("tcp4", ParseUtil.parseIntToIP(row.dwLocalAddr),
127                         ParseUtil.bigEndian16ToLittleEndian(row.dwLocalPort), ParseUtil.parseIntToIP(row.dwRemoteAddr),
128                         ParseUtil.bigEndian16ToLittleEndian(row.dwRemotePort), stateLookup(row.dwState), 0, 0,
129                         row.dwOwningPid));
130             }
131             buf.close();
132         }
133         return conns;
134     }
135 
136     private static List<IPConnection> queryTCPv6Connections() {
137         List<IPConnection> conns = new ArrayList<>();
138         // Get size needed
139         try (CloseableIntByReference sizePtr = new CloseableIntByReference()) {
140             int ret = IPHLP.GetExtendedTcpTable(null, sizePtr, false, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0);
141             // Get buffer and populate table
142             int size = sizePtr.getValue();
143             Memory buf = new Memory(size);
144             do {
145                 ret = IPHLP.GetExtendedTcpTable(buf, sizePtr, false, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0);
146                 if (ret == WinError.ERROR_INSUFFICIENT_BUFFER) {
147                     size = sizePtr.getValue();
148                     buf.close();
149                     buf = new Memory(size);
150                 }
151             } while (ret == WinError.ERROR_INSUFFICIENT_BUFFER);
152             MIB_TCP6TABLE_OWNER_PID tcpTable = new MIB_TCP6TABLE_OWNER_PID(buf);
153             for (int i = 0; i < tcpTable.dwNumEntries; i++) {
154                 MIB_TCP6ROW_OWNER_PID row = tcpTable.table[i];
155                 conns.add(new IPConnection("tcp6", row.LocalAddr, ParseUtil.bigEndian16ToLittleEndian(row.dwLocalPort),
156                         row.RemoteAddr, ParseUtil.bigEndian16ToLittleEndian(row.dwRemotePort), stateLookup(row.State),
157                         0, 0, row.dwOwningPid));
158             }
159             buf.close();
160         }
161         return conns;
162     }
163 
164     private static List<IPConnection> queryUDPv4Connections() {
165         List<IPConnection> conns = new ArrayList<>();
166         // Get size needed
167         try (CloseableIntByReference sizePtr = new CloseableIntByReference()) {
168             int ret = IPHLP.GetExtendedUdpTable(null, sizePtr, false, AF_INET, UDP_TABLE_OWNER_PID, 0);
169             // Get buffer and populate table
170             int size = sizePtr.getValue();
171             Memory buf = new Memory(size);
172             do {
173                 ret = IPHLP.GetExtendedUdpTable(buf, sizePtr, false, AF_INET, UDP_TABLE_OWNER_PID, 0);
174                 if (ret == WinError.ERROR_INSUFFICIENT_BUFFER) {
175                     size = sizePtr.getValue();
176                     buf.close();
177                     buf = new Memory(size);
178                 }
179             } while (ret == WinError.ERROR_INSUFFICIENT_BUFFER);
180             MIB_UDPTABLE_OWNER_PID udpTable = new MIB_UDPTABLE_OWNER_PID(buf);
181             for (int i = 0; i < udpTable.dwNumEntries; i++) {
182                 MIB_UDPROW_OWNER_PID row = udpTable.table[i];
183                 conns.add(new IPConnection("udp4", ParseUtil.parseIntToIP(row.dwLocalAddr),
184                         ParseUtil.bigEndian16ToLittleEndian(row.dwLocalPort), new byte[0], 0, TcpState.NONE, 0, 0,
185                         row.dwOwningPid));
186             }
187             buf.close();
188         }
189         return conns;
190     }
191 
192     private static List<IPConnection> queryUDPv6Connections() {
193         List<IPConnection> conns = new ArrayList<>();
194         // Get size needed
195         try (CloseableIntByReference sizePtr = new CloseableIntByReference()) {
196             int ret = IPHLP.GetExtendedUdpTable(null, sizePtr, false, AF_INET6, UDP_TABLE_OWNER_PID, 0);
197             // Get buffer and populate table
198             int size = sizePtr.getValue();
199             Memory buf = new Memory(size);
200             do {
201                 ret = IPHLP.GetExtendedUdpTable(buf, sizePtr, false, AF_INET6, UDP_TABLE_OWNER_PID, 0);
202                 if (ret == WinError.ERROR_INSUFFICIENT_BUFFER) {
203                     size = sizePtr.getValue();
204                     buf.close();
205                     buf = new Memory(size);
206                 }
207             } while (ret == WinError.ERROR_INSUFFICIENT_BUFFER);
208             MIB_UDP6TABLE_OWNER_PID udpTable = new MIB_UDP6TABLE_OWNER_PID(buf);
209             for (int i = 0; i < udpTable.dwNumEntries; i++) {
210                 MIB_UDP6ROW_OWNER_PID row = udpTable.table[i];
211                 conns.add(
212                         new IPConnection("udp6", row.ucLocalAddr, ParseUtil.bigEndian16ToLittleEndian(row.dwLocalPort),
213                                 new byte[0], 0, TcpState.NONE, 0, 0, row.dwOwningPid));
214             }
215         }
216         return conns;
217     }
218 
219     private static TcpState stateLookup(int state) {
220         switch (state) {
221         case 1:
222         case 12:
223             return CLOSED;
224         case 2:
225             return LISTEN;
226         case 3:
227             return SYN_SENT;
228         case 4:
229             return SYN_RECV;
230         case 5:
231             return ESTABLISHED;
232         case 6:
233             return FIN_WAIT_1;
234         case 7:
235             return FIN_WAIT_2;
236         case 8:
237             return CLOSE_WAIT;
238         case 9:
239             return CLOSING;
240         case 10:
241             return LAST_ACK;
242         case 11:
243             return TIME_WAIT;
244         default:
245             return UNKNOWN;
246         }
247     }
248 }