diff --git a/CHANGES.md b/CHANGES.md
index c03c1be504..5c7be33657 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -20,6 +20,7 @@ Features
* [#959](https://github.com/java-native-access/jna/pull/959): Added `GetProcessTimes` and `GetProcessIoCounters` to `com.sun.jna.platform.win32.Kernel32` - [@dbwiddis](https://github.com/dbwiddis).
* [#952](https://github.com/java-native-access/jna/issues/952): Added `CreateMutex`, `OpenMutex` and `ReleaseMutex` to `com.sun.jna.platform.win32.Kernel32` - [@matthiasblaesing](https://github.com/matthiasblaesing).
* [#973](https://github.com/java-native-access/jna/issues/973): Added `PdhLookupPerfNameByIndex`, `PdhLookupPerfIndexByName`, and `PdhEnumObjectItems` to `c.s.j.platform.win32.Pdh` and a `c.s.j.platform.win32.PdhUtil` class to access them - [@dbwiddis](https://github.com/dbwiddis).
+* [#983](https://github.com/java-native-access/jna/issues/983): Added `GetIfEntry`, `GetIfEntry2`, and `GetNetworkParams` and supporting structures `MIB_IFROW`, `MIB_IF_ROW2`, and `FIXED_INFO` to `c.s.j.platform.win32.IPHlpAPI.java` - [@dbwiddis](https://github.com/dbwiddis).
Bug Fixes
---------
diff --git a/contrib/platform/src/com/sun/jna/platform/win32/IPHlpAPI.java b/contrib/platform/src/com/sun/jna/platform/win32/IPHlpAPI.java
new file mode 100644
index 0000000000..ef3ad94481
--- /dev/null
+++ b/contrib/platform/src/com/sun/jna/platform/win32/IPHlpAPI.java
@@ -0,0 +1,280 @@
+/* Copyright (c) 2018 Daniel Widdis, All Rights Reserved
+ *
+ * The contents of this file is dual-licensed under 2
+ * alternative Open Source/Free licenses: LGPL 2.1 or later and
+ * Apache License 2.0. (starting with JNA version 4.0.0).
+ *
+ * You can freely decide which license you want to apply to
+ * the project.
+ *
+ * You may obtain a copy of the LGPL License at:
+ *
+ * http://www.gnu.org/licenses/licenses.html
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "LGPL2.1".
+ *
+ * You may obtain a copy of the Apache License at:
+ *
+ * http://www.apache.org/licenses/
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "AL2.0".
+ */
+package com.sun.jna.platform.win32;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import com.sun.jna.Structure.FieldOrder;
+import com.sun.jna.platform.win32.Guid.GUID;
+import com.sun.jna.platform.win32.WinDef.UCHAR;
+import com.sun.jna.platform.win32.WinDef.UINT;
+import com.sun.jna.platform.win32.WinDef.ULONG;
+import com.sun.jna.platform.win32.WinDef.ULONGByReference;
+import com.sun.jna.win32.W32APIOptions;
+
+/**
+ * Windows IP Helper API
+ *
+ * @see IP
+ * Helper Reference
+ */
+public interface IPHlpAPI extends Library {
+ IPHlpAPI INSTANCE = Native.load("IPHlpAPI", IPHlpAPI.class, W32APIOptions.DEFAULT_OPTIONS);
+
+ int IF_MAX_STRING_SIZE = 256;
+ int IF_MAX_PHYS_ADDRESS_LENGTH = 32;
+ int MAX_INTERFACE_NAME_LEN = 256;
+ int MAXLEN_IFDESCR = 256;
+ int MAXLEN_PHYSADDR = 8;
+ int MAX_HOSTNAME_LEN = 128;
+ int MAX_DOMAIN_NAME_LEN = 128;
+ int MAX_SCOPE_ID_LEN = 256;
+
+ /**
+ * The MIB_IFROW structure stores information about a particular interface.
+ *
+ * @see MIB_IFROW
+ */
+ @FieldOrder({ "wszName", "dwIndex", "dwType", "dwMtu", "dwSpeed", "dwPhysAddrLen", "bPhysAddr", "dwAdminStatus",
+ "dwOperStatus", "dwLastChange", "dwInOctets", "dwInUcastPkts", "dwInNUcastPkts", "dwInDiscards",
+ "dwInErrors", "dwInUnknownProtos", "dwOutOctets", "dwOutUcastPkts", "dwOutNUcastPkts", "dwOutDiscards",
+ "dwOutErrors", "dwOutQLen", "dwDescrLen", "bDescr" })
+ class MIB_IFROW extends Structure {
+ public char[] wszName = new char[MAX_INTERFACE_NAME_LEN];
+ public int dwIndex;
+ public int dwType;
+ public int dwMtu;
+ public int dwSpeed;
+ public int dwPhysAddrLen;
+ public byte[] bPhysAddr = new byte[MAXLEN_PHYSADDR];
+ public int dwAdminStatus;
+ public int dwOperStatus;
+ public int dwLastChange;
+ public int dwInOctets;
+ public int dwInUcastPkts;
+ public int dwInNUcastPkts;
+ public int dwInDiscards;
+ public int dwInErrors;
+ public int dwInUnknownProtos;
+ public int dwOutOctets;
+ public int dwOutUcastPkts;
+ public int dwOutNUcastPkts;
+ public int dwOutDiscards;
+ public int dwOutErrors;
+ public int dwOutQLen;
+ public int dwDescrLen;
+ public byte[] bDescr = new byte[MAXLEN_IFDESCR];
+ }
+
+ /**
+ * The MIB_IF_ROW2 structure stores information about a particular
+ * interface.
+ *
+ * @see MIB_IF_ROW2
+ */
+ @FieldOrder({ "InterfaceLuid", "InterfaceIndex", "InterfaceGuid", "Alias", "Description", "PhysicalAddressLength",
+ "PhysicalAddress", "PermanentPhysicalAddress", "Mtu", "Type", "TunnelType", "MediaType",
+ "PhysicalMediumType", "AccessType", "DirectionType", "InterfaceAndOperStatusFlags", "OperStatus",
+ "AdminStatus", "MediaConnectState", "NetworkGuid", "ConnectionType", "TransmitLinkSpeed",
+ "ReceiveLinkSpeed", "InOctets", "InUcastPkts", "InNUcastPkts", "InDiscards", "InErrors", "InUnknownProtos",
+ "InUcastOctets", "InMulticastOctets", "InBroadcastOctets", "OutOctets", "OutUcastPkts", "OutNUcastPkts",
+ "OutDiscards", "OutErrors", "OutUcastOctets", "OutMulticastOctets", "OutBroadcastOctets", "OutQLen" })
+ class MIB_IF_ROW2 extends Structure {
+ public long InterfaceLuid; // 64-bit union
+ public ULONG InterfaceIndex;
+ public GUID InterfaceGuid;
+ public char[] Alias = new char[IF_MAX_STRING_SIZE + 1];
+ public char[] Description = new char[IF_MAX_STRING_SIZE + 1];
+ public ULONG PhysicalAddressLength;
+ public UCHAR[] PhysicalAddress = new UCHAR[IF_MAX_PHYS_ADDRESS_LENGTH];
+ public UCHAR[] PermanentPhysicalAddress = new UCHAR[IF_MAX_PHYS_ADDRESS_LENGTH];
+ public ULONG Mtu;
+ public ULONG Type;
+ // enums
+ public int TunnelType;
+ public int MediaType;
+ public int PhysicalMediumType;
+ public int AccessType;
+ public int DirectionType;
+ // 8-bit structure
+ public byte InterfaceAndOperStatusFlags;
+ // enums
+ public int OperStatus;
+ public int AdminStatus;
+ public int MediaConnectState;
+ public GUID NetworkGuid;
+ public int ConnectionType;
+ public long TransmitLinkSpeed;
+ public long ReceiveLinkSpeed;
+ public long InOctets;
+ public long InUcastPkts;
+ public long InNUcastPkts;
+ public long InDiscards;
+ public long InErrors;
+ public long InUnknownProtos;
+ public long InUcastOctets;
+ public long InMulticastOctets;
+ public long InBroadcastOctets;
+ public long OutOctets;
+ public long OutUcastPkts;
+ public long OutNUcastPkts;
+ public long OutDiscards;
+ public long OutErrors;
+ public long OutUcastOctets;
+ public long OutMulticastOctets;
+ public long OutBroadcastOctets;
+ public long OutQLen;
+ }
+
+ /**
+ * The IP_ADDRESS_STRING structure stores an IPv4 address in dotted decimal
+ * notation. The IP_ADDRESS_STRING structure definition is also the type
+ * definition for the IP_MASK_STRING structure.
+ *
+ * @see IP_ADDRESS_STRING
+ */
+ @FieldOrder({ "String" })
+ class IP_ADDRESS_STRING extends Structure {
+ // Null terminated string
+ // up to 3 chars (decimal 0-255) and dot
+ // ending with null
+ public byte[] String = new byte[16];
+ }
+
+ /**
+ * The IP_ADDR_STRING structure represents a node in a linked-list of IPv4
+ * addresses.
+ *
+ * @see IP_ADDR_STRING
+ */
+ @FieldOrder({ "Next", "IpAddress", "IpMask", "Context" })
+ class IP_ADDR_STRING extends Structure {
+ public ByReference Next;
+ public IP_ADDRESS_STRING IpAddress;
+ public IP_ADDRESS_STRING IpMask;
+ public int Context;
+
+ public static class ByReference extends IP_ADDR_STRING implements Structure.ByReference {
+ }
+ }
+
+ /**
+ * The FIXED_INFO structure contains information that is the same across all
+ * the interfaces on a computer.
+ *
+ * @see FIXED_INFO
+ */
+ @FieldOrder({ "HostName", "DomainName", "CurrentDnsServer", "DnsServerList", "NodeType", "ScopeId", "EnableRouting",
+ "EnableProxy", "EnableDns" })
+ class FIXED_INFO extends Structure {
+ public byte[] HostName = new byte[MAX_HOSTNAME_LEN + 4];
+ public byte[] DomainName = new byte[MAX_DOMAIN_NAME_LEN + 4];
+ public IP_ADDR_STRING.ByReference CurrentDnsServer; // IP_ADDR_STRING
+ public IP_ADDR_STRING DnsServerList;
+ public UINT NodeType;
+ public byte[] ScopeId = new byte[MAX_SCOPE_ID_LEN + 4];
+ public UINT EnableRouting;
+ public UINT EnableProxy;
+ public UINT EnableDns;
+
+ public FIXED_INFO(Pointer p) {
+ super(p);
+ read();
+ }
+
+ public FIXED_INFO() {
+ super();
+ }
+ }
+
+ /**
+ * The GetIfEntry function retrieves information for the specified interface
+ * on the local computer.
+ *
+ * The dwIndex member in the MIB_IFROW structure pointed to by the pIfRow
+ * parameter must be initialized to a valid network interface index
+ * retrieved by a previous call to the GetIfTable, GetIfTable2, or
+ * GetIfTable2Ex function. The GetIfEntry function will fail if the dwIndex
+ * member of the MIB_IFROW pointed to by the pIfRow parameter does not match
+ * an existing interface index on the local computer.
+ *
+ * @param pIfRow
+ * A pointer to a MIB_IFROW structure that, on successful return,
+ * receives information for an interface on the local computer.
+ * On input, set the dwIndex member of MIB_IFROW to the index of
+ * the interface for which to retrieve information.
+ * @return If the function succeeds, the return value is NO_ERROR.
+ */
+ int GetIfEntry(MIB_IFROW pIfRow);
+
+ /**
+ * The GetIfEntry2 function retrieves information for the specified
+ * interface on the local computer.
+ *
+ * On input, at least one of the following members in the MIB_IF_ROW2
+ * structure passed in the Row parameter must be initialized: InterfaceLuid
+ * or InterfaceIndex. The fields are used in the order listed above. So if
+ * the InterfaceLuid is specified, then this member is used to determine the
+ * interface. If no value was set for the InterfaceLuid member (the value of
+ * this member was set to zero), then the InterfaceIndex member is next used
+ * to determine the interface. On output, the remaining fields of the
+ * MIB_IF_ROW2 structure pointed to by the Row parameter are filled in.
+ *
+ * @param pIfRow2
+ * A pointer to a MIB_IF_ROW2 structure that, on successful
+ * return, receives information for an interface on the local
+ * computer. On input, the InterfaceLuid or the InterfaceIndex
+ * member of the MIB_IF_ROW2 must be set to the interface for
+ * which to retrieve information.
+ * @return If the function succeeds, the return value is NO_ERROR.
+ */
+ int GetIfEntry2(MIB_IF_ROW2 pIfRow2);
+
+ /**
+ * The GetNetworkParams function retrieves network parameters for the local
+ * computer.
+ *
+ * @param pFixedInfo
+ * A pointer to a buffer that contains a FIXED_INFO structure
+ * that receives the network parameters for the local computer,
+ * if the function was successful. This buffer must be allocated
+ * by the caller prior to calling the GetNetworkParams function.
+ * @param pOutBufLen
+ * A pointer to a ULONG variable that specifies the size of the
+ * FIXED_INFO structure. If this size is insufficient to hold the
+ * information, GetNetworkParams fills in this variable with the
+ * required size, and returns an error code of
+ * ERROR_BUFFER_OVERFLOW.
+ * @return If the function succeeds, the return value is ERROR_SUCCESS.
+ */
+ int GetNetworkParams(FIXED_INFO pFixedInfo, ULONGByReference pOutBufLen);
+}
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/IPHlpAPITest.java b/contrib/platform/test/com/sun/jna/platform/win32/IPHlpAPITest.java
new file mode 100644
index 0000000000..eacf58b256
--- /dev/null
+++ b/contrib/platform/test/com/sun/jna/platform/win32/IPHlpAPITest.java
@@ -0,0 +1,137 @@
+/* Copyright (c) 2018 Daniel Widdis, All Rights Reserved
+ *
+ * The contents of this file is dual-licensed under 2
+ * alternative Open Source/Free licenses: LGPL 2.1 or later and
+ * Apache License 2.0. (starting with JNA version 4.0.0).
+ *
+ * You can freely decide which license you want to apply to
+ * the project.
+ *
+ * You may obtain a copy of the LGPL License at:
+ *
+ * http://www.gnu.org/licenses/licenses.html
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "LGPL2.1".
+ *
+ * You may obtain a copy of the Apache License at:
+ *
+ * http://www.apache.org/licenses/
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "AL2.0".
+ */
+package com.sun.jna.platform.win32;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigInteger;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+import com.sun.jna.Memory;
+import com.sun.jna.platform.win32.IPHlpAPI.FIXED_INFO;
+import com.sun.jna.platform.win32.IPHlpAPI.MIB_IFROW;
+import com.sun.jna.platform.win32.IPHlpAPI.MIB_IF_ROW2;
+import com.sun.jna.platform.win32.WinDef.ULONG;
+
+public class IPHlpAPITest {
+
+ @Test
+ public void testGetIfEntry() throws SocketException {
+ Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netint : Collections.list(interfaces)) {
+ if (!netint.isLoopback() && netint.getHardwareAddress() != null) {
+ // Create new MIB_IFROW, set index to this interface index
+ MIB_IFROW ifRow = new MIB_IFROW();
+ ifRow.dwIndex = netint.getIndex();
+ assertEquals(WinError.NO_ERROR, IPHlpAPI.INSTANCE.GetIfEntry(ifRow));
+ // Bytes should exceed packets
+ // These originate from unsigned ints. Use standard Java
+ // widening conversion to long which does sign-extension,
+ // then drop any copies of the sign bit, to prevent the value
+ // being considered a negative one by Java if it is set
+ long bytesSent = (ifRow.dwOutOctets) & 0xffffffffL;
+ long packetsSent = (ifRow.dwOutUcastPkts) & 0xffffffffL;
+ if (packetsSent > 0) {
+ assertTrue(bytesSent > packetsSent);
+ } else {
+ assertEquals(0, bytesSent);
+ }
+ long bytesRecv = (ifRow.dwInOctets) & 0xffffffffL;
+ long packetsRecv = (ifRow.dwInUcastPkts) & 0xffffffffL;
+ if (packetsRecv > 0) {
+ assertTrue(bytesRecv > packetsRecv);
+ } else {
+ assertEquals(0, bytesRecv);
+ }
+ }
+ }
+
+ }
+
+ @Test
+ public void testGetIfEntry2() throws SocketException {
+ byte majorVersion = Kernel32.INSTANCE.GetVersion().getLow().byteValue();
+ if (majorVersion >= 6) {
+ Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netint : Collections.list(interfaces)) {
+ if (!netint.isLoopback() && netint.getHardwareAddress() != null) {
+ // Create new MIB_IFROW2, set index to this interface index
+ MIB_IF_ROW2 ifRow = new MIB_IF_ROW2();
+ ifRow.InterfaceIndex = new ULONG(netint.getIndex());
+ assertEquals(WinError.NO_ERROR, IPHlpAPI.INSTANCE.GetIfEntry2(ifRow));
+ // Bytes should exceed packets
+ // These originate from unsigned longs.
+ BigInteger bytesSent = new BigInteger(Long.toHexString(ifRow.OutOctets), 16);
+ BigInteger packetsSent = new BigInteger(Long.toHexString(ifRow.OutUcastPkts), 16);
+ if (packetsSent.longValue() > 0) {
+ assertEquals(1, bytesSent.compareTo(packetsSent));
+ } else {
+ assertEquals(0, bytesSent.compareTo(packetsSent));
+ }
+ BigInteger bytesRecv = new BigInteger(Long.toHexString(ifRow.InOctets), 16);
+ BigInteger packetsRecv = new BigInteger(Long.toHexString(ifRow.InUcastPkts), 16);
+ if (packetsRecv.longValue() > 0) {
+ assertEquals(1, bytesRecv.compareTo(packetsRecv));
+ } else {
+ assertEquals(0, bytesRecv.compareTo(packetsRecv));
+ }
+ }
+ }
+ } else {
+ System.err.println("testGetIfEntery2 test can only be run on Vista or later.");
+ }
+ }
+
+ @Test
+ public void testGetNetworkParams() {
+ Pattern ValidIP = Pattern.compile("^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
+
+ WinDef.ULONGByReference bufferSize = new WinDef.ULONGByReference();
+ assertEquals(WinError.ERROR_BUFFER_OVERFLOW, IPHlpAPI.INSTANCE.GetNetworkParams(null, bufferSize));
+ FIXED_INFO buffer = new FIXED_INFO(new Memory(bufferSize.getValue().longValue()));
+ assertEquals(WinError.ERROR_SUCCESS, IPHlpAPI.INSTANCE.GetNetworkParams(buffer, bufferSize));
+
+ // Check all DNS servers are valid IPs
+ IPHlpAPI.IP_ADDR_STRING dns = buffer.DnsServerList;
+ while (dns != null) {
+ // Start with 16-char byte array
+ String addr = new String(dns.IpAddress.String);
+ // addr String will have length 16; trim off trailing null(s)
+ int nullPos = addr.indexOf(0);
+ if (nullPos != -1) {
+ addr = addr.substring(0, nullPos);
+ }
+ // addr is now a dotted-notation IP string. Test valid
+ assertTrue(ValidIP.matcher(addr).matches());
+ dns = dns.Next;
+ }
+ }
+}