diff --git a/src/dorkbox/netUtil/Dns.kt b/src/dorkbox/netUtil/Dns.kt index 2d4d57a..9db69e8 100644 --- a/src/dorkbox/netUtil/Dns.kt +++ b/src/dorkbox/netUtil/Dns.kt @@ -1,33 +1,42 @@ package dorkbox.netUtil +import com.sun.jna.Memory +import com.sun.jna.Pointer.* +import com.sun.jna.platform.win32.WinError import dorkbox.executor.Executor -import java.io.BufferedWriter -import java.io.File -import java.io.FileWriter -import java.io.IOException +import dorkbox.netUtil.jna.windows.IPHlpAPI +import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_ADDRESSES_LH +import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_DNS_SERVER_ADDRESS_XP +import java.io.* +import java.net.* +import java.nio.file.Files +import java.nio.file.Paths +import java.util.* +import javax.naming.Context +import javax.naming.NamingException +import javax.naming.directory.DirContext +import javax.naming.directory.InitialDirContext + -/** - * - */ object Dns { /** * @throws IOException if the DNS resolve.conf file cannot be read */ fun setDNSServers(dnsServersString: String) { if (Common.OS_LINUX) { - val dnsServers = dnsServersString.split(","); - val dnsFile = File("/etc/resolvconf/resolv.conf.d/head"); + val dnsServers = dnsServersString.split(",") + val dnsFile = File("/etc/resolvconf/resolv.conf.d/head") if (!dnsFile.canRead()) { - throw IOException("Unable to initialize dns server file. Something is SERIOUSLY wrong"); + throw IOException("Unable to initialize dns server file. Something is SERIOUSLY wrong") } BufferedWriter(FileWriter(dnsFile)).use { - it.write("# File location: /etc/resolvconf/resolv.conf.d/head\n"); - it.write("# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)\n"); + it.write("# File location: /etc/resolvconf/resolv.conf.d/head\n") + it.write("# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)\n") dnsServers.forEach { dns -> - it.write("nameserver $dns\n"); + it.write("nameserver $dns\n") } it.flush(); @@ -38,4 +47,331 @@ object Dns { throw RuntimeException("NOT IMPL.") } } + + + /** Returns all located name servers, which may be empty. */ + val defaultNameServers: List by lazy { + val nameServers = getUnsortedDefaultNameServers() + + if (IPv6.isPreferred) { + // prefer IPv6: return IPv6 first, then IPv4 (each in the order added) + nameServers.filter { it.address is Inet6Address } + nameServers.filter { it.address is Inet4Address } + } else if (IPv4.isPreferred) { + // skip IPv6 addresses + nameServers.filter { it.address is Inet4Address } + } + + // neither is specified, return in the order added + nameServers + } + + // largely from: + // https://github.com/dnsjava/dnsjava/blob/fb4889ee7a73f391f43bf6dc78b019d87ae15f15/src/main/java/org/xbill/DNS/config/BaseResolverConfigProvider.java#L22 + private fun getUnsortedDefaultNameServers() : List { + val defaultNameServers = mutableListOf() + + // Using jndi-dns to obtain the default name servers. + // + // See: + // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html + // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html + + // Using jndi-dns to obtain the default name servers. + // + // See: + // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html + // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html + val env = Hashtable() + env[Context.INITIAL_CONTEXT_FACTORY] = "com.sun.jndi.dns.DnsContextFactory" + env["java.naming.provider.url"] = "dns://" + try { + val ctx: DirContext = InitialDirContext(env) + val dnsUrls = ctx.environment["java.naming.provider.url"] as String? + val servers = dnsUrls!!.split(" ".toRegex()).toTypedArray() + + for (server in servers) { + try { + defaultNameServers.add(Common.socketAddress(URI(server).host, 53)) + } catch (e: URISyntaxException) { + Common.logger.debug("Skipping a malformed nameserver URI: {}", server, e) + } + } + } catch (ignore: NamingException) { + // Will also try JNA/etc if this fails. + } + + if (defaultNameServers.isNotEmpty()) { + return defaultNameServers + } + + if (Common.OS_WINDOWS) { + // have to use JNA to access the WINDOWS resolver info + + // modified from: https://github.com/dnsjava/dnsjava/blob/master/src/main/java/org/xbill/DNS/config/WindowsResolverConfigProvider.java + // The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a 15KB working buffer + var buffer: Memory? = Memory(15 * 1024) + val size: com.sun.jna.ptr.IntByReference = com.sun.jna.ptr.IntByReference(0) + + val flags: Int = IPHlpAPI.GAA_FLAG_SKIP_UNICAST or + IPHlpAPI.GAA_FLAG_SKIP_ANYCAST or + IPHlpAPI.GAA_FLAG_SKIP_MULTICAST or + IPHlpAPI.GAA_FLAG_SKIP_FRIENDLY_NAME + + var error: Int = IPHlpAPI.GetAdaptersAddresses( + IPHlpAPI.AF_UNSPEC, + flags, + NULL, + buffer, + size + ) + + if (error == WinError.ERROR_BUFFER_OVERFLOW) { + buffer = Memory(size.value.toLong()) + error = IPHlpAPI.GetAdaptersAddresses( + IPHlpAPI.AF_UNSPEC, + flags, + NULL, + buffer, + size + ) + } + + if (error == WinError.ERROR_SUCCESS) { + var result: IP_ADAPTER_ADDRESSES_LH? = IP_ADAPTER_ADDRESSES_LH(buffer) + while (result != null) { + + // only interfaces with IfOperStatusUp + if (result.OperStatus == 1) { + var dns: IP_ADAPTER_DNS_SERVER_ADDRESS_XP? = result.FirstDnsServerAddress + while (dns != null) { + + var address: InetAddress + try { + address = dns.Address.toAddress() + if (address is Inet4Address || !address.isSiteLocalAddress) { + defaultNameServers.add(InetSocketAddress(address, 53)) + } else { + Common.logger.debug("Skipped site-local IPv6 server address {} on adapter index {}", address, result.IfIndex) + } + } catch (e: UnknownHostException) { + Common.logger.warn("Invalid nameserver address on adapter index {}", result.IfIndex, e) + } + dns = dns.Next + } + } + + result = result.Next + } + } + } else { + // try resolve.conf + + // first try the default unix config path + var tryParse = tryParseResolvConfNameservers("/etc/resolv.conf") + if (!tryParse.first) { + // then fallback to netware + tryParse = tryParseResolvConfNameservers("sys:/etc/resolv.cfg") + } + + defaultNameServers.addAll(tryParse.second) + } + + return defaultNameServers + } + + private fun tryParseResolvConfNameservers(path: String): Pair> { + val p = Paths.get(path) + if (Files.exists(p)) { + try { + Files.newInputStream(p).use { `in` -> + return Pair(true, parseResolvConfNameservers(`in`)) + } + } catch (e: IOException) { + // ignore + } + } + return Pair(false, mutableListOf()) + } + + private fun parseResolvConfNameservers(`in`: InputStream): MutableList { + val defaultNameServers = mutableListOf() + + InputStreamReader(`in`).use { isr -> + BufferedReader(isr).use { br -> + var line: String? + while (br.readLine().also { line = it } != null) { + val st = StringTokenizer(line) + if (!st.hasMoreTokens()) { + continue + } + when (st.nextToken()) { + "nameserver" -> defaultNameServers.add(InetSocketAddress(st.nextToken(), 53)) + } + } + } + } + + return defaultNameServers + } + + /** + * Gets the threshold for the number of dots which must appear in a name before it is considered + * absolute. The default is 1. + */ + val numberDots: Int by lazy { + if (Common.OS_WINDOWS) { + 1 + } else { + // first try the default unix config path + var tryParse = tryParseResolvConfNDots("/etc/resolv.conf") + if (!tryParse.first) { + // then fallback to netware + tryParse = tryParseResolvConfNDots("sys:/etc/resolv.cfg") + } + tryParse.second + } + } + + private fun tryParseResolvConfNDots(path: String): Pair { + val p = Paths.get(path) + if (Files.exists(p)) { + try { + Files.newInputStream(p).use { `in` -> + return Pair(true, getNdots(`in`)) + } + } catch (e: IOException) { + // ignore + } + } + return Pair(false, 1) + } + + + // @kotlin.jvm.Throws(IOException::class) +// private fun parseResolvConf(`in`: InputStream) { +// InputStreamReader(`in`).use { isr -> +// BufferedReader(isr).use { br -> +// +// var line: String? +// loop@ while (br.readLine().also { line = it } != null) { +// val st = StringTokenizer(line) +// if (!st.hasMoreTokens()) { +// continue +// } +// when (st.nextToken()) { +// "domain" -> { +// // man resolv.conf: +// // The domain and search keywords are mutually exclusive. If more than one instance of +// // these keywords is present, the last instance wins. +// searchlist.clear() +// if (!st.hasMoreTokens()) { +// continue@loop +// } +// addSearchPath(st.nextToken()) +// } +// "search" -> { +// // man resolv.conf: +// // The domain and search keywords are mutually exclusive. If more than one instance of +// // these keywords is present, the last instance wins. +// searchlist.clear() +// while (st.hasMoreTokens()) { +// addSearchPath(st.nextToken()) +// } +// } +// } +// } +// } +// } +// +// // man resolv.conf: +// // The search keyword of a system's resolv.conf file can be overridden on a per-process basis by +// // setting the environment variable LOCALDOMAIN to a space-separated list of search domains. +// val localdomain = System.getenv("LOCALDOMAIN") +// if (localdomain != null && !localdomain.isEmpty()) { +// searchlist.clear() +// parseSearchPathList(localdomain, " ") +// } +// } + + // @kotlin.jvm.Throws(IOException::class) + private fun getNdots(`in`: InputStream): Int { + var ndots = 1 + + InputStreamReader(`in`).use { isr -> + BufferedReader(isr).use { br -> + var line: String? + loop@ while (br.readLine().also { line = it } != null) { + val st = StringTokenizer(line) + if (!st.hasMoreTokens()) { + continue + } + + when (st.nextToken()) { + "domain" -> { + // man resolv.conf: + // The domain and search keywords are mutually exclusive. If more than one instance of + // these keywords is present, the last instance wins. + if (!st.hasMoreTokens()) { + continue@loop + } + } + "search" -> { + // man resolv.conf: + // The domain and search keywords are mutually exclusive. If more than one instance of + // these keywords is present, the last instance wins. + while (st.hasMoreTokens()) { + val token = st.nextToken() + if (token.startsWith("ndots:")) { + ndots = parseNdots(token.substring(6)) + } + } + } + "options" -> while (st.hasMoreTokens()) { + val token = st.nextToken() + if (token.startsWith("ndots:")) { + ndots = parseNdots(token.substring(6)) + } + } + } + } + } + } + + // man resolv.conf: + // The options keyword of a system's resolv.conf file can be amended on a per-process basis by + // setting the environment variable RES_OPTIONS to a space-separated list of resolver options as + // explained above under options. + val resOptions = System.getenv("RES_OPTIONS") + if (resOptions != null && !resOptions.isEmpty()) { + val st = StringTokenizer(resOptions, " ") + while (st.hasMoreTokens()) { + val token = st.nextToken() + if (token.startsWith("ndots:")) { + ndots = parseNdots(token.substring(6)) + } + } + } + + return ndots + } + + private fun parseNdots(token: String): Int { + if (token.isNotEmpty()) { + try { + var ndots = token.toInt() + if (ndots >= 0) { + if (ndots > 15) { + // man resolv.conf: + // The value for this option is silently capped to 15 + ndots = 15 + } + return ndots + } + } catch (e: NumberFormatException) { + // ignore + } + } + + return 1 + } } diff --git a/src/dorkbox/netUtil/jna/JnaHelper.kt b/src/dorkbox/netUtil/jna/JnaHelper.kt new file mode 100644 index 0000000..85c5203 --- /dev/null +++ b/src/dorkbox/netUtil/jna/JnaHelper.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2016 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.netUtil.jna + +import com.sun.jna.Library +import com.sun.jna.Native +import com.sun.jna.NativeLibrary +import com.sun.jna.win32.W32APIOptions +import dorkbox.netUtil.Common +import java.util.* + +/** + * Helper method to get the library info from JNA when registering via direct map + */ +object JnaHelper { + @kotlin.jvm.Throws(IllegalArgumentException::class) + fun register(libraryName: String, clazz: Class<*>): NativeLibrary { + val options: MutableMap = HashMap() + options[Library.OPTION_CLASSLOADER] = clazz.classLoader + + if (Common.OS_WINDOWS) { + val entries: Set> = W32APIOptions.DEFAULT_OPTIONS.entries + for ((key, value) in entries) { + options[key] = value + } + } + + val library = NativeLibrary.getInstance(libraryName, options) + ?: throw IllegalArgumentException("$libraryName doesn't exist or cannot be loaded.") + + Native.register(clazz, library) + return library + } +} diff --git a/src/dorkbox/netUtil/jna/windows/IPHlpAPI.kt b/src/dorkbox/netUtil/jna/windows/IPHlpAPI.kt new file mode 100644 index 0000000..77953e3 --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/IPHlpAPI.kt @@ -0,0 +1,37 @@ +package dorkbox.netUtil.jna.windows + +import com.sun.jna.Pointer +import com.sun.jna.ptr.IntByReference +import dorkbox.netUtil.jna.JnaHelper.register + +/** + * https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/ + */ +object IPHlpAPI { + init { + register("IPHlpAPI", IPHlpAPI::class.java) + } + + const val AF_UNSPEC = 0 + const val AF_INET = 2 + const val AF_INET6 = 23 + const val GAA_FLAG_SKIP_UNICAST = 0x0001 + const val GAA_FLAG_SKIP_ANYCAST = 0x0002 + const val GAA_FLAG_SKIP_MULTICAST = 0x0004 + const val GAA_FLAG_SKIP_DNS_SERVER = 0x0008 + const val GAA_FLAG_INCLUDE_PREFIX = 0x0010 + const val GAA_FLAG_SKIP_FRIENDLY_NAME = 0x0020 + const val GAA_FLAG_INCLUDE_WINS_INFO = 0x0040 + const val GAA_FLAG_INCLUDE_GATEWAYS = 0x0080 + const val GAA_FLAG_INCLUDE_ALL_INTERFACES = 0x0100 + const val GAA_FLAG_INCLUDE_ALL_COMPARTMENTS = 0x0200 + const val GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER = 0x0400 + + external fun GetAdaptersAddresses( + family: Int, + flags: Int, + reserved: Pointer?, + adapterAddresses: Pointer?, + sizePointer: IntByReference? + ): Int +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_ADDRESSES_LH.java b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_ADDRESSES_LH.java new file mode 100644 index 0000000..6565bb4 --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_ADDRESSES_LH.java @@ -0,0 +1,119 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid; + +public +class IP_ADAPTER_ADDRESSES_LH extends Structure { + public static + class ByReference extends IP_ADAPTER_ADDRESSES_LH implements Structure.ByReference {} + + + public int Length; + public int IfIndex; + + public IP_ADAPTER_ADDRESSES_LH.ByReference Next; + public String AdapterName; + + public IP_ADAPTER_UNICAST_ADDRESS_LH.ByReference FirstUnicastAddress; + public IP_ADAPTER_ANYCAST_ADDRESS_XP.ByReference FirstAnycastAddress; + public IP_ADAPTER_MULTICAST_ADDRESS_XP.ByReference FirstMulticastAddress; + public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference FirstDnsServerAddress; + + public WString DnsSuffix; + public WString Description; + public WString FriendlyName; + + public byte[] PhysicalAddress = new byte[8]; + public int PhysicalAddressLength; + public int Flags; + public int Mtu; + public int IfType; + public int OperStatus; + public int Ipv6IfIndex; + + public int[] ZoneIndices = new int[16]; + public Pointer FirstPrefix; + + public long TransmitLinkSpeed; + public long ReceiveLinkSpeed; + + public Pointer FirstWinsServerAddress; + public Pointer FirstGatewayAddress; + + public int Ipv4Metric; + public int Ipv6Metric; + + public LUID Luid; + public SOCKET_ADDRESS Dhcpv4Server; + public int CompartmentId; + public Guid.GUID NetworkGuid; + + public int ConnectionType; + public int TunnelType; + public SOCKET_ADDRESS Dhcpv6Server; + + public byte[] Dhcpv6ClientDuid = new byte[130]; + public int Dhcpv6ClientDuidLength; + public int Dhcpv6Iaid; + + public IP_ADAPTER_DNS_SUFFIX.ByReference FirstDnsSuffix; + + public + IP_ADAPTER_ADDRESSES_LH(Pointer p) { + super(p); + read(); + } + + public + IP_ADAPTER_ADDRESSES_LH() {} + + @Override + protected + List getFieldOrder() { + return Arrays.asList("Length", + "IfIndex", + "Next", + "AdapterName", + "FirstUnicastAddress", + "FirstAnycastAddress", + "FirstMulticastAddress", + "FirstDnsServerAddress", + "DnsSuffix", + "Description", + "FriendlyName", + "PhysicalAddress", + "PhysicalAddressLength", + "Flags", + "Mtu", + "IfType", + "OperStatus", + "Ipv6IfIndex", + "ZoneIndices", + "FirstPrefix", + "TransmitLinkSpeed", + "ReceiveLinkSpeed", + "FirstWinsServerAddress", + "FirstGatewayAddress", + "Ipv4Metric", + "Ipv6Metric", + "Luid", + "Dhcpv4Server", + "CompartmentId", + "NetworkGuid", + "ConnectionType", + "TunnelType", + "Dhcpv6Server", + "Dhcpv6ClientDuid", + "Dhcpv6ClientDuidLength", + "Dhcpv6Iaid", + "FirstDnsSuffix"); + } +} + + diff --git a/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_ANYCAST_ADDRESS_XP.java b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_ANYCAST_ADDRESS_XP.java new file mode 100644 index 0000000..dd70026 --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_ANYCAST_ADDRESS_XP.java @@ -0,0 +1,25 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public +class IP_ADAPTER_ANYCAST_ADDRESS_XP extends Structure { + public static + class ByReference extends IP_ADAPTER_ANYCAST_ADDRESS_XP implements Structure.ByReference {} + + + public int Length; + public int Reserved; + + public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference Next; + public SOCKET_ADDRESS Address; + + @Override + protected + List getFieldOrder() { + return Arrays.asList("Length", "Reserved", "Next", "Address"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_DNS_SERVER_ADDRESS_XP.java b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_DNS_SERVER_ADDRESS_XP.java new file mode 100644 index 0000000..75ba6fb --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_DNS_SERVER_ADDRESS_XP.java @@ -0,0 +1,25 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public +class IP_ADAPTER_DNS_SERVER_ADDRESS_XP extends Structure { + public static + class ByReference extends IP_ADAPTER_DNS_SERVER_ADDRESS_XP implements Structure.ByReference {} + + + public int Length; + public int Reserved; + + public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference Next; + public SOCKET_ADDRESS Address; + + @Override + protected + List getFieldOrder() { + return Arrays.asList("Length", "Reserved", "Next", "Address"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_DNS_SUFFIX.java b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_DNS_SUFFIX.java new file mode 100644 index 0000000..2b93f8b --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_DNS_SUFFIX.java @@ -0,0 +1,22 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public +class IP_ADAPTER_DNS_SUFFIX extends Structure { + public static + class ByReference extends IP_ADAPTER_DNS_SUFFIX implements Structure.ByReference {} + + + public IP_ADAPTER_DNS_SUFFIX.ByReference Next; + public char[] _String = new char[256]; + + @Override + protected + List getFieldOrder() { + return Arrays.asList("Next", "_String"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_MULTICAST_ADDRESS_XP.java b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_MULTICAST_ADDRESS_XP.java new file mode 100644 index 0000000..4357dfb --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_MULTICAST_ADDRESS_XP.java @@ -0,0 +1,25 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public +class IP_ADAPTER_MULTICAST_ADDRESS_XP extends Structure { + public static + class ByReference extends IP_ADAPTER_MULTICAST_ADDRESS_XP implements Structure.ByReference {} + + + public int Length; + public int Reserved; + + public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference Next; + public SOCKET_ADDRESS Address; + + @Override + protected + List getFieldOrder() { + return Arrays.asList("Length", "Reserved", "Next", "Address"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_UNICAST_ADDRESS_LH.java b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_UNICAST_ADDRESS_LH.java new file mode 100644 index 0000000..e89ff31 --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/IP_ADAPTER_UNICAST_ADDRESS_LH.java @@ -0,0 +1,44 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public +class IP_ADAPTER_UNICAST_ADDRESS_LH extends Structure { + public static + class ByReference extends IP_ADAPTER_UNICAST_ADDRESS_LH implements Structure.ByReference {} + + + public int Length; + public int IfIndex; + + public IP_ADAPTER_UNICAST_ADDRESS_LH.ByReference Next; + public SOCKET_ADDRESS Address; + + public int PrefixOrigin; + public int SuffixOrigin; + public int DadState; + + public int ValidLifetime; + public int PreferredLifetime; + public int LeaseLifetime; + public byte OnLinkPrefixLength; + + @Override + protected + List getFieldOrder() { + return Arrays.asList("Length", + "IfIndex", + "Next", + "Address", + "PrefixOrigin", + "SuffixOrigin", + "DadState", + "ValidLifetime", + "PreferredLifetime", + "LeaseLifetime", + "OnLinkPrefixLength"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/LUID.java b/src/dorkbox/netUtil/jna/windows/structs/LUID.java new file mode 100644 index 0000000..ce0563e --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/LUID.java @@ -0,0 +1,18 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public +class LUID extends Structure { + public int LowPart; + public int HighPart; + + @Override + protected + List getFieldOrder() { + return Arrays.asList("LowPart", "HighPart"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/SOCKADDR_IN.java b/src/dorkbox/netUtil/jna/windows/structs/SOCKADDR_IN.java new file mode 100644 index 0000000..7927cec --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/SOCKADDR_IN.java @@ -0,0 +1,27 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public +class SOCKADDR_IN extends Structure { + public short sin_family; + public short sin_port; + public byte[] sin_addr = new byte[4]; + public byte[] sin_zero = new byte[8]; + + public + SOCKADDR_IN(Pointer p) { + super(p); + read(); + } + + @Override + protected + List getFieldOrder() { + return Arrays.asList("sin_family", "sin_port", "sin_addr", "sin_zero"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/SOCKADDR_IN6.java b/src/dorkbox/netUtil/jna/windows/structs/SOCKADDR_IN6.java new file mode 100644 index 0000000..8997716 --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/SOCKADDR_IN6.java @@ -0,0 +1,29 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public +class SOCKADDR_IN6 extends Structure { + public short sin6_family; + public short sin6_port; + public int sin6_flowinfo; + + public byte[] sin6_addr = new byte[16]; + public int sin6_scope_id; + + public + SOCKADDR_IN6(Pointer p) { + super(p); + read(); + } + + @Override + protected + List getFieldOrder() { + return Arrays.asList("sin6_family", "sin6_port", "sin6_flowinfo", "sin6_addr", "sin6_scope_id"); + } +} diff --git a/src/dorkbox/netUtil/jna/windows/structs/SOCKET_ADDRESS.java b/src/dorkbox/netUtil/jna/windows/structs/SOCKET_ADDRESS.java new file mode 100644 index 0000000..8048ddd --- /dev/null +++ b/src/dorkbox/netUtil/jna/windows/structs/SOCKET_ADDRESS.java @@ -0,0 +1,39 @@ +package dorkbox.netUtil.jna.windows.structs; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public +class SOCKET_ADDRESS extends Structure { + public static final int AF_INET = 2; + public static final int AF_INET6 = 23; + + public Pointer lpSockaddr; + public int iSockaddrLength; + + public + InetAddress toAddress() throws UnknownHostException { + switch (((int) lpSockaddr.getShort(0))) { + case AF_INET: + SOCKADDR_IN in4 = new SOCKADDR_IN(lpSockaddr); + return InetAddress.getByAddress(in4.sin_addr); + case AF_INET6: + SOCKADDR_IN6 in6 = new SOCKADDR_IN6(lpSockaddr); + return Inet6Address.getByAddress("", in6.sin6_addr, in6.sin6_scope_id); + } + + return null; + } + + @Override + protected + List getFieldOrder() { + return Arrays.asList("lpSockaddr", "iSockaddrLength"); + } +}